手写系列 | 实现一个Promise.all()(并行)

97 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

关于 Promise.all()

如果熟悉 Promise 的话,想必多多少少有用过或者了解 Promise.all(),Promise.all() 用于并行执行异步操作,即在所有异步操作执行完后才执行回调。

比较常见的场景就是需要调用多个接口,没有顺序的要求,但要获取到所有接口返回的数据之后再进行下一步操作。

  • 【参数】:Promise.all() 参数是一个 promise 数组,数组里是Promise实例。
  • 【结果】:执行后的返回值是一个promise,因为可以进行 promise.all().then()
  • 【特点】:
    • 在执行时,所有的 promise 对象都成功,才会触发成功,有一个失败就会返回失败
    • 如果参数中包含非 promise 的值,这些值会被忽略(Promise完成时,返回数组中这些值依旧存在)

代码实现

思路:

  1. 定义一个返回 Promise 的函数,参数为数组类型
  2. 判断传参是否是数组,如果不是,则 reject()
  3. 创建一个数组,用于存储执行后返回的数据,再用一个变量来进行异步执行计数(为了保证最后全部执行完毕后再返回)
  4. 遍历传递过来的 Promise 数组,Promise.resolve() 将异步的执行结果传递给 .then(),并计数。
  5. 所有异步执行完毕之后,resolve()传递最终结果
function promiseAll(array) {
    return new Promise((resolve,reject) => {
        if(!(array instanceof Array)) {
            reject(new Error("参数不是有效的数组类型"))
        }

        let resultArr = new Array(array.length);
        let count = 0;
        for(let i=0; i<array.length; i++) {
            Promise.resolve(array[i]).then( res => {
                count++;
                resultArr[i] = res;
                if(count === array.length) {
                    resolve(resultArr)
                }
            }).catch(err => reject(err))
        }
    })
}

接下来定义一个Promise数组的生成器,setTimeout设置不同的执行时间,来模拟接口的执行(输出数组会看到是一个由多个 pending 状态的 Promise 组成的数组)

将生成的 Promise 数组传入写好的 PromiseAll()函数中,通过.then()来统一获取返回值

const promiseGenerator = (num) => {
    return new Array(num).fill(0).map((item, index) => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(index)
            }, Math.random() * 1000)
        })
    })
}

const proAllArr = promiseGenerator(5)
console.log('proAllArr:',proAllArr)
promiseAll(proAllArr).then(console.log) // [ 0, 1, 2, 3, 4 ]

运行结果

[ 0, 1, 2, 3, 4 ] 即为 count === array.length 时,传递给.then() 的统一返回值。