要实现Promise.all(),首先我们得知道这个函数的接受什么、做了什么和返回了什么。
引用MDN的描述:
Promise.all() 方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个Promise实例, 那个输入的所有promise的resolve回调的结果是一个数组。这个Promise的resolve回调执行是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的时候。它的reject回调执行是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。
总结一下就是:
输入:一个promise的iterable类型
输出:一个Promise,resolve为输入的所有promise的resolve回调的结果数组,reject为输入promise中的抛出的错误信息或输入不合法。
测试用例如下:
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
const promise4 = new Promise((resolve, reject) => setTimeout(reject, 101, 'reject'))
Promise.all([promise1, promise2, promise3]).then(values => console.log(values));
Promise.all([promise1, promise4, promise3]).catch(err => console.log(err));
Promise.all(8).catch(err => console.log(err));
> TypeError: number 8 is not iterable (cannot read property Symbol(Symbol.iterator))
> Array [3, 42, "foo"]
> "reject"
这里可以看到 3行输出,可能有人会对输出顺序感到疑惑,但这篇博客只讲Promise.all的实现,所以不做过多讨论。
开始写代码
首先创建一个函数,接受一个输入参数 promises,返回一个Promise 对象
const myPromiseAll = function(promises){
return new Promise((resolve,reject)=>{
})
}
接着,我们要开始迭代这个变量。
const myPromiseAll = (promises) => {
return new Promise((resolve, reject) => {
let resolves = []
const promisesLen = promises.length
let promisesI = 0
const next = () => {
promises[promisesI].then(res => {
resolves.push(res)
promisesI += 1
if (promisesI === promisesLen) {
resolve(resolves) // 如果 索引超出边界 resolve
} else{
next() // 迭代执行
}
}).catch(err => {
reject(err) // 遇到错误 reject
})
}
next()
})
}
这里默认promises是promise集合,所以 promise2 用例通过不了。这里我们就要加一些判断。
提示:
使用promise instanceof Promise
可以判断对象是否是 Promise 的实例
修复代码:
const myPromiseAll = (promises) => {
return new Promise((resolve, reject) => {
let resolves = []
const promisesLen = promises.length
let promisesI = 0
const next = () => {
const nowPormise = promises[promisesI]
if (nowPormise instanceof Promise) { // 判断是否为Promise
nowPormise.then(res => {
resolves.push(res)
promisesI += 1
if (promisesI === promisesLen) {
resolve(resolves) // 如果 索引超出边界 resolve
}else{
next() // 迭代执行
}
}).catch(err => {
reject(err) // 遇到错误 reject
})
}else{
resolves.push(nowPormise)
promisesI += 1
if (promisesI === promisesLen) {
resolve(resolves)
}else{
next()
}
}
}
next()
})
}
我们还要判断输入是否为可迭代对象。那么如何判断呢?获取,我们可以从报错信息获取灵感:
TypeError: number 8 is not iterable (cannot read property Symbol(Symbol.iterator))
报错提示我们,8不是一个可迭代对象 原因是,无法读取到 Symbol.iterator 属性。
所以,我们可以通过检查对象是否有 Symbol.iterator对象来判断对象是否为 可迭代对象。
- 使用 in 操作符
- 使用 hasOwnProperty(Symbol.iterator)
- Array 是 Object
但是,这里我们要注意的是8不是对象,8是Number。所以,我们进行两步判断,判断对象和判断可迭代。
const myPromiseAll = (promises) => {
return new Promise((resolve, reject) => {
let resolves = []
const promisesLen = promises.length
let promisesI = 0
const next = () => {
const promiseType = typeof promises
if(promiseType === "object" ){ // 判断是否为对象
if(Symbol.iterator in promises){ // 判断是否可迭代
const nowPormise = promises[promisesI]
if (nowPormise instanceof Promise) { // 判断是否为Promise
nowPormise.then(res => {
resolves.push(res)
promisesI += 1
if (promisesI === promisesLen) {
resolve(resolves) // 如果 索引超出边界 resolve
}else{
next() // 迭代执行
}
}).catch(err => {
reject(err) // 遇到错误 reject
})
}else{
resolves.push(nowPormise)
promisesI += 1
if (promisesI === promisesLen) {
resolve(resolves) // 如果 索引超出边界 resolve
}else{
next()
}
}
}else{
reject(`TypeError: ${promiseType} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`)
}
}else{
reject(`TypeError: ${promiseType} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`)
}
}
next()
})
}
我们可以美化一下代码,改写一些 逻辑,让代码更清晰:
const myPromiseAll = (promises) => {
return new Promise((resolve, reject) => {
let resolves = []
const promisesLen = promises.length
let promisesI = 0
const check = (res) => {
resolves.push(res)
if (++promisesI === promisesLen) {
resolve(resolves) // 如果 索引超出边界 resolve
} else {
next() // 迭代执行
}
}
const next = () => {
const promiseType = typeof promises
if (promiseType === "object" && Symbol.iterator in promises) { // 判断是否为可迭代对象
const nowPormise = promises[promisesI]
if (nowPormise instanceof Promise) { // 判断是否为Promise
nowPormise.then(res => {
check(res)
}).catch(err => {
reject(err) // 遇到错误 reject
})
} else {
check(nowPormise)
}
}
else {
reject(`TypeError: ${promiseType} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`)
}
}
next()
})
}
测试一下:
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
const promise4 = new Promise((resolve, reject) => setTimeout(reject, 101, 'reject'))
// myPormiseAll ...
myPromiseAll([promise1, promise2, promise3]).then(values => console.log(values));
myPromiseAll([promise1, promise4, promise3]).catch(err => console.log(err));
myPromiseAll(8).catch(err => console.log(err));
Nice!
总结一下:
- 需要知道 Promise.all 做了什么
- 在 then 里递归,来依次捕获
- 如何判断 可迭代对象
原创不易!如果对你有帮助,还请点赞收藏。
如果有疑惑可以在下方留言。
如果有什么建议,可以私信我。