实现一个Promise.all,了解它到底在做什么

本文详细解释了Promise.all的工作原理,通过手动实现和MDN文档的对照,探讨了Promise对象的使用和任务并发处理。作者还提供了一个示例,展示了Promise.all如何处理多个任务的同步和异步执行。

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

Promise.all到底在做什么,根据MDN的描述,试着实现一下

/**
 * Promise工具函数_withResolvers, 返回一个对象:
 * 其中包含一个Promise实例: promise,以及用来改变这个实例promise状态的 resolve 和 reject 函数
 * 由于node环境不支持Promise.withResolvers(),所以我们选择手动实现
 * @returns {promise, resolve, reject}
 */
Promise._withResolvers = function _withResolvers() {
    let resolve;
    let reject;
    let promise = new Promise((_resolve, _reject) => {
        resolve = _resolve;
        reject = _reject;
    });

    return {promise, resolve, reject};
}

/**
 * 判断入参是否为PromiseLike: 是ES官方定义实现的Promise实例 or 符合Promise A+规范 --带有一个then方法的 promise
 * @param {*} target 
 * @returns 
 */
function isPromiseLike(target) {
    return target && typeof target === 'function';
}
/**
 * Promise.all 的显示实现
 * @param {*} tasks  tasks is an iterable object: Array、Set...
 */
Promise._all = function _all(tasks) {

    // 在node环境下需要自己实现 withResolvers
    // promise是_all方法的返回值,
    const {promise, resolve, reject} = Promise._withResolvers(); 
    let total = 0; // 一共有多少个待执行的任务
    let finished = 0; // 已完成的任务数量
    let result = []; // 返回值,当所有的任务正确完成后,按任务的加入顺序进行放置每一个Promise

    // 因为Promise.all的参数要求是一个可迭代对象,所以可以用 for of 进行遍历
    for(const task of tasks) {
        let index = total++; // 总任务数量+1, 并记录每个任务的索引
        /**
         * 注意每一次进行迭代时,我们都单独声明一个处理函数_resolve,用于操作result
         * .then()中_resolve函数的操作是异步执行的,调用_resolve 时,total 已经能代表总的任务数量了
         * 但由于 _resolve 创建时,可以访问外部作用域,所以,index就代表了从 0 开始的结果下标
         * 我们不知道result[index]何时被赋值,也不知道哪一个index是最后设置的
         */
        const _resolve = res => {
            // 当任务正确结束的时候,根据传入的顺序放置结果,并且记录已经完成的总数量
            result[index] = res;
            finished++;
            if(finished === total) { // _resolve 方法执行时,这里的total已经能代表tasks的总数量了
                resolve(result);
            }
        }
        /**
         *  每一轮迭代,Promise.resolve(task)会创建一个新的Promise
         *  如果这个Promise被完成,则调用_resolve函数,将其加入结果集
         *  如果这个Promise被拒绝,则会调用reject
         */
        Promise.resolve(task).then(_resolve, reject);
    }

    // 如果任务列表为空,直接将promise的状态设置为fulfilled
    if(total === 0) {
        resolve(result);
    }
    return promise;
}

我们将官网的用例拿来测试一下:

const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 100);
});

Promise._all([p1, p2, p3]).then((values) => {
  console.log(values); // [3, 1337, "foo"]
});

ok,完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值