![e254ff90d0dee8141aa54a367f7cc148.png](https://img-blog.csdnimg.cn/img_convert/e254ff90d0dee8141aa54a367f7cc148.png)
离上次写了 Promise.all 实现之后,已经隔了,呃,快一年了...
为什么又想起来写 Promise 的其他静态方法的实现呢?原因是最近已经连续两次收到了某跳动公司的面试邀请了。想着虽然不一定去面,但还是要把一些面试的八股文给熟悉一下,毕竟又写了快一年的业务代码了,自己的长进也是模模糊糊,好像也没有落到具体的东西上...
首先我们先回顾下 Promise.all 静态方法的实现
rencoo:[面试题]实现Promise.allzhuanlan.zhihu.com![bc0327e9d68380fc0083929d715b40cb.png](https://img-blog.csdnimg.cn/img_convert/bc0327e9d68380fc0083929d715b40cb.png)
记得当时写完之后,知乎上有个哥们 Rambo 还热心地指出我的问题,并且他自己也写了更加完美的实现
Rambo:Promise.all实现zhuanlan.zhihu.com接下来呢,我们就来看看现在要实现的 Promise.allSettled 静态方法
还是从它的用法入手:
相对于 Promise.all 需要所有 promise都成功时才 resolve或者有一个失败时即reject,Promise.allSettled 只关心所有 promise 是不是都被 settle 了,不管其是 rejected状态的 promise,还是非 rejected状态(即fulfilled)的 promise, 我都可以拿到它的最终状态并对其进行处理
Promise.allSettled 的结果数组中可能包含以下两种格式的数据
- {status:"fulfilled", value:result} 对于成功的响应
- {status:"rejected", reason:error} 对于 error
参照原理写的 Promise.all 的实现,我们根据 Promise.allSettled 的概念,稍作改变就可以写出实现
if (!Promise.allSettled) {
Promise.allSettled = function (promises) {
return new Promise(resolve => {
const data = [], len = promises.length;
let count = len;
for (let i = 0; i < len; i += 1) {
const promise = promises[i];
promise.then(res => {
data[i] = { status: 'fulfilled', value: res };
}, error => {
data[i] = { status: 'rejected', reason: error };
}).finally(() => { // promise has been settled
if (!--count) {
resolve(data);
}
});
}
});
}
}
测试下
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise2, promise1];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "rejected"
// "fulfilled"
另外,更加简便的,我们也可以根据 Promise.all 来实现 Promise.allSettled
思路是让 Promise.all 入参中的所有 promise 都映射为新的最终状态为 fulfilled 的 promise (而新promise的result则根据原来promise的状态为fulfilled/reject来决定),这样就总是能让Promise.all 成功返回数组
if (!Promise.allSettled) {
Promise.allSettled = function (promises) {
return Promise.all(promises.map(p => Promise.resolve(p).then(res => {
return { status: 'fulfilled', value: res }
}, error => {
return { status: 'rejected', reason: error }
})));
};
}
最后,再对之前 Promise.all 的实现做个修正!!! 当时的实现有误,对所有 promise 是否都被 settled 的判断是错误的,不能用结果的长度来判断。把原来的那几个测试 promise 顺序一颠倒就能看出问题来了,最后一个最快被 settled,按照长度来判定的话就 resolve 出去了,但这样是不对的,因为最后的那个 promise 最快 settled 时,前面几个慢的还没有被 settled,这时候时不能 resolve 最终结果的
function all(promises) {
let len = promises.length, res = []
// 修正
let count = len
if (len) {
return new Promise(function (resolve, reject) {
for(let i=0; i<len; i++) {
let promise = promises[i];
promise.then(response => {
res[i] = response
// 当返回结果为最后一个时
// 修正
// if (res.length === len) {
if (!--count) {
resolve(res)
}
}, error => {
reject(error)
})
}
})
}
}
// test
let p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1)
// reject(new Error('error 1'))
}, 3000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2)
}, 2000)
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(3)
}, 1000)
})
all([p1, p2, p3]).then(res => {
console.log(res) //(*)
}).catch(console.log)
// 修正前*处
// console:[empty × 2, 3]
// 修正后*处
// console: [1, 2, 3]
最后想说的是,技术是件严谨的事情,我们需要对自己的输出有责任心,捂脸...