如何实现一个Promise.all
# 0.前言
Promise
系列先写起来,这道题目来自B站up主山月被头条面试出的一道考题,相比于传统的手写Promise.all()
加了很多限制条件,题目如下:
满足
Promise.al
l的所有规则传入的所有 Promsie 都是 fulfilled,则返回由他们的值组成的,状态为 fulfilled 的新 Promise;
只要有一个 Promise 是 rejected,则返回 rejected 状态的新 Promsie,且它的值是第一个 rejected 的 Promise 的值;
只要有一个 Promise 是 pending,则返回一个 pending 状态的新 Promise;
除了传统的条件之外,仍需满足以下两个条件:
- 传入一个
Iterable
的对象,不一定是数组。 - 要求并行执行。
- 传入一个
# 1.测试代码:
await Promise.all([1, Promise.resolve(2)]);
//-> [1, 2]
await Promise.all([1, Promise.reject(2)]);
//-> Throw Error: 2
2
3
4
5
测试函数的并行执行,需要实现一个sleep
函数
// 输入时间,返回Promise对象
// 一开始是pending状态,过了seconds秒后,变为resolve状态
const sleep = (seconds) =>
new Promise((resolve) => setTimeout(() => resolve(seconds), seconds));
2
3
4
使用以下示例代码测试:
pAll([1, 2, 3]).then((o) => console.log(o));
pAll([sleep(3000), sleep(2000), sleep(1000)]).then((o) => console.log(o));
pAll([sleep(3000), sleep(2000), sleep(1000), Promise.reject(10000)])
.then((o) => console.log(o))
.catch((e) => console.log(e, "<- Error"));
2
3
4
5
# 2.答案
# 山月版本:
const pAll = function(_promisesArr){
const result = [];
let count = 0
return new Promise((resolve,reject)=>{
// + 第一步: 将 Iterable => Array
const promiseArray = Array.from(_promises)
// + 使用 for 循环去遍历数组 | 当然也可以使用 forEach
for(let i = 0;i<promiseArr.length;i++){
// 注意直接使用 promise[i].then 是错误的,因为还有考虑返回的结果不是Promise的情况
Promise.resolve(promise[i]).then(val=>{
count++
result[i] = value
if(count === promiseArr.length){
resolve(result)
}
},err =>{
reject(err)
})
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 常规版:
Promise.all = function(promiseArr) {
let index = 0, result = []
return new Promise((resolve, reject) => {
promiseArr.forEach((p, i) => {
Promise.resolve(p).then(val => {
index++
result[i] = val
if (index === promiseArr.length) {
resolve(result)
}
}, err => {
reject(err)
})
})
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3.需要注意的知识点
使用
for...of
是最值得推荐的去遍历Iterable
对象的方法。观察一个对象是否
Iterable
,只需要打印出来,是否具有[Symbol.iterator]
属性即可,具体内容详见:阮一峰Iterator章节 (opens new window),讲的非常清楚。限制条件二,要求遍历是并行的,这一点在之前我写的《深入理解forEach与forof》,以及《forEach/for...in/of》对于各种类型的数据该用什么样的遍历方式,以及对应的遍历的特性已经讲解的很清楚了。
在对
promiseArr
进行循环时,一直写的是下面第一种写法,特此注释加以记忆。promise[i].then
(只能处理Promise对象
)Promise.resolve(promise[i]).then
(可以处理处理非Promise
对象)
// 第一种方法只能通过: Promise.all([Promise.resolve(1),Promise.resolve(2)]) // 而无法通过 非Promise对象 Promise.all([1,2,3])
1
2
3
4进一步解释:为啥在外面包一层
Promise.resolve
就可以了呢?因为:
Promise
在实现时,就已经考虑到对于这种纯结果(官网的表述是:if x is not an object or function
)的处理,详见:PromiseA+ (opens new window)的2.3.4
规范。ps.其实
Promise
在实现这块时也不难,直接return new Promise(xxxx)
就可以解决了,难的在于2.3.3
如果x
是一个thenable
的对象,这块始终是云里雾里。