引言
本文从四个方面循序渐进介绍 Promise.allSettled
:
-
Promise.all()
的缺陷 - 引入
Promise.allSettled()
-
Promise.allSettled()
与Promise.all()
各自的适用场景 - 手写
Promise.allSettled()
实现
下面正文开始👇
Promise.all() 的缺陷
我们在之前的一篇文章中已经介绍过,当我们使用 Promise.all()
执行过个 promise
时,只要其中任何一个promise
失败都会执行 reject
,并且 reject
的是第一个抛出的错误信息,只有所有的 promise
都 resolve
时才会调用 .then
中的成功回调
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = new Promise((resolve, reject) => {
setTimeout(reject, 1000, 'three');
});
Promise.all([p1, p2, p3])
.then(values => {
console.log('resolve: ', values)
}).catch(err => {
console.log('reject: ', err)
})
// reject: three
注意: 其中任意一个 promise
被 reject
,Promise.all
就会立即被 reject
,数组中其它未执行完的 promise
依然是在执行的, Promise.all
没有采取任何措施来取消它们的执行
但大多数场景中,我们期望传入的这组 promise
无论执行失败或成功,都能获取每个 promise
的执行结果,为此,ES2020 引入了 Promise.allSettled()
Promise.allSettled()
Promise.allSettled()
可以获取数组中每个 promise
的结果,无论成功或失败
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = new Promise((resolve, reject) => {
setTimeout(reject, 1000, 'three');
});
Promise.allSettled([p1, p2, p3])
.then(values => {
console.log(values)
})
/*
[
{status: "fulfilled", value: 1},
{status: "fulfilled", value: 2},
{status: "rejected", reason: "three"}
]
*/
当浏览器不支持 Promise.allSettled
,可以如此 polyfill:
if (!Promise.allSettled) {
const rejectHandler = reason => ({status: "rejected", reason})
const resolveHandler = value => ({status: "fulfilled", value})
Promise.allSettled = promises =>
Promise.all(
promises.map((promise) =>
Promise.resolve(promise)
.then(resolveHandler, rejectHandler)
)
// 每个 promise 需要用 Promise.resolve 包裹下
// 以防传递非 promise
);
}
// 使用
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = new Promise((resolve, reject) => {
setTimeout(reject, 1000, 'three');
})
const promises = [p1, p2, p3]
Promise.allSettled(promises).then(console.log)
Promise.allSettled() 与 Promise.all() 各自的适用场景
Promise.allSettled()
更适合:
- 彼此不依赖,其中任何一个被
reject
,对其它都没有影响 - 期望知道每个
promise
的执行结果
Promise.all()
更适合:
- 彼此相互依赖,其中任何一个被
reject
,其它都失去了实际价值
手写 Promise.allSettled 源码
与 Promise.all
不同的是,当 promise
被 reject
之后,我们不会直接 reject
,而是记录下该 reject
的值和对应的状态 'rejected'
;
同样地,当 promise
对象被 resolve
时我们也不仅仅局限于记录值,同时也会记录状态 'fulfilled'
。
当所有的 promise
对象都已执行(解决或拒绝),我们统一 resolve
所有的 promise
执行结果数组
MyPromise.allSettled = function (promises) {
return new MyPromise((resolve, reject) => {
promises = Array.isArray(promises) ? promises : []
let len = promises.length
const argslen = len
// 如果传入的是一个空数组,那么就直接返回一个resolved的空数组promise对象
if (len === 0) return resolve([])
// 将传入的参数转化为数组,赋给args变量
let args = Array.prototype.slice.call(promises)
// 计算当前是否所有的 promise 执行完成,执行完毕则resolve
const compute = () => {
if(--len === 0) {
resolve(args)
}
}
function resolvePromise(index, value) {
// 判断传入的是否是 promise 类型
if(value instanceof MyPromise) {
const then = value.then
then.call(value, function(val) {
args[index] = { status: 'fulfilled', value: val}
compute()
}, function(e) {
args[index] = { status: 'rejected', reason: e }
compute()
})
} else {
args[index] = { status: 'fulfilled', value: value}
compute()
}
}
for(let i = 0; i < argslen; i++){
resolvePromise(i, args[i])
}
})
}
总结
彼此相互依赖,一个失败全部失效(全无或全有)用 Promise.all
;相互独立,获取每个结果用 Promise.allSettled
网友评论