关于promise的教程很多了,个人也看了不少,但心中总是没有一个清晰的脉络,总感觉知识点有点杂乱,希望在这里做个记录,简单清晰全面地总结promise的用法及注意点,不废话不深入,便于查阅
创建及使用promise
创建
1 | const p = new Promise((resolve, reject) => { |
promise创建的时候立即执行参数函数
promise的状态
promise有三种状态:
- Pending:代表初始状态,结果未知
- Resolved:结果成功返回(
resolve
被调用) - Rejected:计算过程中发生错误(
reject
被调用或者参数函数执行过程中有异常抛出throw
)
如果promise变成Resolved或Rejected,我们称promise已经稳定了(settled),稳定之后promise的状态不再变化:
消费/查询/使用promise
一般都要关心promise的执行结果:
1 | // onFulfilled处理resolved通知,onRejected处理rejected通知 |
我们可以随时查询promise,无论它当时的状态是否是稳定的
promise始终是异步的
即使你查询promise的时候promise的状态已经是稳定的,你传递的通知函数也会被异步调用:
1 | Promise.resolve(1).then(value => console.log(value)) |
其他创建promise的方法
const p = Promise.resolve(x)
- x是普通值:p是一个处于Resolved状态且值为x的promise
- x也是通过resolve创建的promise:p = x
- x是其他方式创建的promise:p的状态取决于x的状态,就是说你查询p和查询x是等价的
const p = Promise.reject(error)
返回一个处于Rejected状态且错误为error的promise:
1 | const myError = new Error('Problem!'); |
promise链接(Chaining Promises)
promise的一大优点是可以链接,因为Promise.then
的返回值还是一个promise:
1 | const q = p.then(onFulfilled, onRejected) // q is a new promise |
所以我们可以继续查询q:
1 | p |
使用普通值来resolve q
1 | p |
使用另一个promise来resolve q
1 | p |
resolve promise with normal value or another promise
状态示意图:
resolve q from onRejected
从onRejected
中返回的值一样可以用来resolve(不是reject) q:
1 | p |
通过抛出异常来reject q(这个时候没有reject方法可以调用)
1 | p |
Promise.prototype.then
的总结:
One of the onFulfilled and onRejected handlers will be executed to handle the current promise’s fulfillment or rejection. The call always happens asynchronously, even when the current promise is already settled. The behavior of the returned promise (call it p) depends on the handler’s execution result, following a specific set of rules. If the handler function:
- returns a value: p gets fulfilled with the returned value as its value.
- doesn’t return anything: p gets fulfilled with undefined as its value.
- throws an error: p gets rejected with the thrown error as its value.
- returns an already fulfilled promise: p gets fulfilled with that promise’s value as its value.
- returns an already rejected promise: p gets rejected with that promise’s value as its value.
- returns another pending promise: p is pending and becomes fulfilled/rejected with that promise’s value as its value immediately after that promise becomes fulfilled/rejected.
错误随链接传递直到catch
1 | asyncFunc1() |
错误处理要点
不要使用then方法来捕获错误
1 | // Don’t do this |
注意区分reject和throw
一般来说reject抛出的是可预期的操作错误,如文件不存在、网络断开等;而throw抛出的是代码错误,如参数类型错误等
Promise中抛出的异常不能被try-catch捕获
1 | try { |
promise组合
Promise.all([promise1, promise2, …])
1 | Promise.all([ |
Promise.race([promise1, promise2, …])
1 | Promise.race([ |
两个额外的promise函数
done()
前面说过promise中抛出的异常外界捕获不到,所以我们解决这个问题(始终加上catch也不行,catch中也可能抛出异常),可以在promise上定义一个done
方法,然后跟在promise链最后面:
1 | function doSomething() { |
可以这样来定义done方法:
1 | Promise.prototype.done = function (onFulfilled, onRejected) { |
finally()
仿造try-catch-finally
我们可以在promise上定义一个finally方法,使得无论最后promise状态如何,始终执行一个callback:
1 | Promise.prototype.finally = function (callback) { |
Notably, like JS finally clause:
- no value or reason is passed in to the finally code (i.e. to callback).
- Does not affect the result, i.e. the resulting promise is still fulfilled with value or rejected with reason, just like the original one, unless…
- If the finally code blows up (i.e. if callback throws, or returns a rejected promise), it propagates that failure onward, instead of the original success or failure.
更新
最新的Promise规范已经包含这个方法了:Promise.finally
async & await
async & await本质上是promise的语法糖,掌握了promise基本知道它们的用法了,这个需要注意的是async函数始终返回一个promise:
1 | async function asyncFunc() { |
1 | async function asyncFunc() { |
1 | async function asyncFunc() { |
1 | async function asyncFunc() { |
1 | async function asyncFunc() { |
注意:一个async函数asyncFunc有下面4种调用方法:
- asyncFunc()
- await asyncFunc()
- return asyncFunc()
- return await asyncFunc()
它们的意义各不相同,参考下面的博文
参考:
http://exploringjs.com/es6/ch_promises.html
http://exploringjs.com/es2016-es2017/ch_async-functions.html
https://jakearchibald.com/2017/await-vs-return-vs-return-await/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then