要实现Promise.all(),首先我们得知道这个函数的接受什么、做了什么和返回了什么。 引用[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)的描述: > 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中的抛出的错误信息或输入不合法。 测试用例如下: ```javascript 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 对象 ```javascript const myPromiseAll = function(promises){ return new Promise((resolve,reject)=>{ }) } ``` > 接着,我们要开始迭代这个变量。 ```javascript 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 的实例 修复代码: ```javascript 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对象来判断对象是否为 可迭代对象。 > 1. 使用 in 操作符 > 2. 使用 hasOwnProperty(Symbol.iterator) > 3. Array 是 Object 但是,这里我们要注意的是8不是对象,8是Number。所以,我们进行两步判断,判断对象和判断可迭代。 ```javascript 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() }) } ``` 我们可以美化一下代码,改写一些 逻辑,让代码更清晰: ```javascript 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() }) } ``` 测试一下: ```javascript 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! 总结一下: 1. 需要知道 Promise.all 做了什么 2. 在 then 里递归,来依次捕获 3. 如何判断 可迭代对象 原创不易!如果对你有帮助,还请点赞收藏。 如果有疑惑可以在下方留言。 如果有什么建议,可以私信我。 --- 转载至CSDN博主:[[李唐敏民](https://blog.csdn.net/qq_39041210 "李唐敏民")] ----------------------- --- 最后修改:2024 年 06 月 18 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏
2 条评论
文章结构紧凑,层次分明,逻辑严密,让人一读即懂。
你的文章充满了智慧,让人敬佩。 http://www.55baobei.com/eqDRiJBGES.html