前言
Promise.all常用于处理多个并行的异步操作。
Promise.all可接受一个可迭代对象,一般为Promise组成的数组,并在所有Promise都完成或遇到第一个rejected后返回结果。
本文将通过实验,验证Promise.all的几个特性与实用场景下的优化
本文依旧以setTimeout代表异步操作,实验涉及四个异步操作,如下:
// 四个promise封装的异步动作,其中三个返回resolve,一个返回reject
function asyncAction1() {
let p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('1');
}, 3000);
});
return p;
}
function asyncAction2() {
let p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('2');
}, 2000);
});
return p;
}
function asyncAction3() {
let p = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('3');
}, 1000);
});
return p;
}
function asyncAction4() {
let p = new Promise(function(resolve, reject) {
setTimeout(function() {
reject('error');
}, 4000);
});
return p;
}
返回顺序与完成时间
promise.all返回数据的顺序和参数列表申明顺序相同,与异步完成时间无关。
尽管返回数据时,是按顺序的,但是执行时,所有promise都是并行的,promise.all在所有promise都fullfilled的情况下,完成时间以最晚fullfilled的promise为准。
console.log(new Date());
promiseArr = [asyncAction1(), asyncAction2(), asyncAction3()];
Promise.all(promiseArr).then(res => {
console.log(res);
console.log(new Date());
});
输出:
rejected情况处理
promise.all只返回一个promise,所以当遇到第一个rejected的promise时,就会立即返回rejected,其他fullfilled的promise即使执行了也不会返回。
promiseArr = [asyncAction1(), asyncAction2(), asyncAction3(), asyncAction4()];
Promise.all(promiseArr)
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
输出:
实际场景与改良
有时候,我们使用Promise.all同时发起若干异步请求,但并不希望因为一个失败而放弃其余请求,此时我们可以对promise队列进行改动,最终使得rejected信息也以fullfilled的状态输出。
当catch捕获rejected信息后,promise的链条就恢复了,我们可以在catch中将错误信息进行返回,那么下一步then返回的promise将会成为fufilled状态,错误信息将成为resolve回调函数的参数。
asyncAction4()
.catch(err => {
return err;
})
.then(res => {
console.log(res);
});
输出:
因此我们可以通过map方法对promiseArr中返回rejected状态的promise进行上述处理,保证promise.all始终能够完成,而错误信息也出现在resolve回调函数的参数数组中。
Promise.all(promiseArr.map(p => p.catch(err => err)))
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
输出: