最近在面试时遇到了一个很经典的手写题,实现一个 Promise.all
(虽然经常听到 手写 Promise 手写 Promise.all 这种面试题,但是很惭愧一直没有深入了解 - -| 不晓得里面的很多细节问题,所以写的问题还是很大的。但是,俗话说的好,种一棵树最好的时间是十年前,其次就是现在 ^ ^
首先,
Promise.all用法都清楚,一般都是传入一个Promise数组,在then回调里拿到所有Promise的返回值数组,然后去使用。很重要的一点是
Promise.all最终返回的也是一个Promise!!! 这点不要忘了至于入参,经过了解知道它不一定是个数组,我们可以传入任意一种可迭代对象,比如
Set Map String Array类型都都是可以的,所以入参进来后需要判断并统一处理入参还有一种情况,是可迭代对象,但是长度为
0,这时需要直接resolve空数组返回的这个
Promise的执行结果,则取决于传入的所有Promise的执行结果,全部成功返回的Promise结果才会是resolve,否则reject结合以上思路的需要注意的问题点,可以写出以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32Promise.myAll = function (promises) {
// 先校验参数是否为可迭代对象
if (typeof promises[Symbol.iterator] !== 'function') {
return new Promise((_, reject) => {
reject(new TypeError(`${typeof promises} is not iterable (cannot read property Symbol(Symbol.iterator))`));
});
}
let pmsArr = [...promises] // 先把可迭代对象转成数组,方便统一处理
let results = []; // 存放每个 promise 执行结果
let length = pmsArr.length;
let didCount = 0;
// 处理空数组:立即 resolve 空数组
if (length === 0) {
return Promise.resolve([]);
}
// 注意!!!是返回一个promise,而不是执行结果
return new Promise((resolve, reject) => {
for (let i = 0; i < pmsArr.length; i++) {
Promise.resolve(pmsArr[i]).then(res => {
results[i] = res;
didCount++;
// 执行完了再 resolve 结果数组
if (didCount === length) {
resolve(results);
}
}, err => {
// 有一个失败就 reject
reject(err);
})
}
})
}