手撕Promise.all()

本文介绍了Promise.all()的功能和工作原理,通过MDN的描述总结了其输入、输出特点。并通过示例代码展示了如何手动实现Promise.all(),包括判断输入是否为Promise实例和可迭代对象的过程。最后进行了代码优化并提供了测试案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要实现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对象来判断对象是否为 可迭代对象。

  1. 使用 in 操作符
  2. 使用 hasOwnProperty(Symbol.iterator)
  3. 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!

总结一下:

  1. 需要知道 Promise.all 做了什么
  2. 在 then 里递归,来依次捕获
  3. 如何判断 可迭代对象

原创不易!如果对你有帮助,还请点赞收藏。
如果有疑惑可以在下方留言。
如果有什么建议,可以私信我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值