Promise 和 Async 函数学习与使用总结
工作中对 Promise 和 Async 函数的一些使用心得
Promise
- resolve 若返回的是另一个 promise,则另一个 promise 的状态决定了前一个 promise 的状态
1 | const p1 = new Promise((resolve, reject) => { |
- resolve 或 reject 不会终止 promise 参数函数的执行
1 | new Promise((resolve, reject) => { |
可以在 resolve 或 reject 前加上 return 来防止执行后面代码的意外。
- promise 对象一旦创建就会立即执行
Promise.prototype.then
then 方法第一个参数是 resolved 状态的回调函数,第二个参数 (可选) 是 rejected 状态的回调函数
then 方法若 return 一个新的 promise 对象,则下一个 then 方法会等待这个新的 promise 对象状态改变之后才会调用
Promise.prototype.catch
catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法。
1 | const p1 = new Promise((resolve, reject) => { |
- catch 方法后还可以继续调用 catch 方法来捕获上一个 catch 中的错误。
Promise.prototype.finally 方法
用于指定不管 Promise 对象最后状态如何,都会执行的操作。
finally方法的回调函数不接受任何参数,表明finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
Promise.resolve
这个方法将现有对象转为 Promise 对象,该方法的执行结果根据参数不同有四种不同的情况
参数为一个 promise 实例,这时该方法原封不动的返回这个实例
参数不是具有 then 方法的对象,或根本不是对象(如原始值),则该方法返回一个新的 promise 对象,状态为 resolved
1
2
3
4const p = Promise.resolve('hello');
p.then((result) => {
console.log(result); // => hello
});参数为一个 thenable 对象,方法会将其转为 Promise 对象,然后立即执行 thenable 的 then 方法
1
2
3
4
5
6
7
8
9let thenable = {
then: function (resolve, reject) {
resolve(2);
},
};
let p1 = Promise.resolve(thenable);
p1.then((result) => {
console.log(result); // => 2
});方法中不带有参数,这时直接返回一个状态为 resolved 的 promise 对象
1
2
3
4const p = Promise.resolve();
p.then(() => {
//...
});需要注意的是,立即
resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。(这话不好理解,待之后更新说明)
Promise.reject
同 resolve 一样返回一个 promise 实例,该实例状态为 rejected。
与 Promise.resolve 不一样的是,Promise.reject 方法的参数会作为后续方法的参数。
1 | const thenable = { |
上面代码中 catch 方法的参数不是 reject 抛出的 error 字符串,而是 thenable 对象。
Promise.all
将多个 Promise 实例,包装成一个新的 Promise 实例
1 | const p = Promise.all([p1, p2, p3]); |
p1、p2、p3 都是 Promise 实例,如果不是,则会默认调用 Promise.resolve()
方法的参数可以不是数组,但必须具有 Iterator 接口。
p 的状态由 p1、p2、p3 决定,分成两种情况:
- 只有 p1、p2、p3 状态都变为 resolve,p 的状态才会 resolve,此时三个返回值组成数组传递给 p 的回调函数
- 如果其中一个变为 rejected ,p 的状态就为 rejected,第一个被 reject 的返回值会传递给 p 的回调函数
若参数中的 promise 实例都具有 catch 方法,则不会调用 Promise.all 的 catch 方法,因为执行完 catch 方法后会被 resolved
promise.all 的错误处理
Promise.all 方法中如果其中一个 promise 的状态为 reject,则其他 resolve 状态的 promise 不会返回。
若想接收所有状态的结果,可以对每个参数里的每个 promise 都加上 catch ,这样就不会进 Promise.all 的 catch ,实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22var promiseArray = [];
promiseArray.push(promiseResove(1)); // 状态为 resolve 的 promise
promiseArray.push(promiseReject(3)); // 状态为 reject 的 promise
promiseArray.push(promiseResove(2)); // 状态为 resolve 的 promise
// 将传入promise.all的数组进行遍历,如果catch住reject结果,
// 直接返回,这样就可以在最后结果中将所有结果都获取到
var handlePromise = Promise.all(
promiseArray.map(function (promiseItem) {
return promiseItem.catch(function (err) {
return err;
});
})
);
handlePromise
.then(function (values) {
console.log('all promise are resolved', values);
})
.catch(function (reason) {
// 不会走到 catch
console.log('promise reject failed reason', reason);
});
Async/Await
使用注意点
async 函数返回的是一个 promise 对象,可以使用
then方法添加回调函数。async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。即只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。await命令后面,可以是 Promise 对象或原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
错误处理
await 后的异步操作出错,等同于 async 函数返回的 promise 被 reject 。
await 后面的 Promise 对象若变为 reject 状态 ,reject 参数会被 async 函数返回值的 catch 方法接收到,且当任何一个 await 后的 Promise 对象变为 reject,整个 async 函数都会中断执行,相当于 return 了这个状态为 reject 的 promise
1 | aysnc function f() { |
若希望前一个异步操作失败后不中断后续的异步操作,可将 await 放在 try catch 里。或者给 await 后的 Promise 加一个 catch 方法。
1 | // 1. |
- await 后返回的是个 promise ,使用下面这种方式来进行错误处理更简洁.
1 | // await-to.js 简易实现 |
避免 async/await 滥用
分享一些关于 async/await 使用的讨论:
精读《async/await 是把双刃剑》。文章底部有 github 上的一个讨论链接。