Promise 的异步任务并发函数

Posted by CodingWithAlice on March 7, 2025

Promise 的异步任务并发函数

Promise.race 传入的数组可以动态变化么?

// 解答标题:可以动态数组,即使第一次执行 Promise.race([A,B,C]) 时每个 promise 都被触发了,Promise.race([B,C,D]) 还是可以返回第一个 非待定状态的 promise

Promise 的异步任务并发函数 4个:Promise.all Promise.allsettled Promise.race Promise.any

  • Promise.race [总是异步的] - 返回第一个非待定状态的 Promise
    • 若传入空数组,则返回的 promise 一直保持 pending 状态
    • 若传入的数组全是已经敲定的 promise ,但返回的 promise 依旧是异步敲定
    • 若传入 非Promise值xN 或 已经敲定的 promisexN,则找到第一个这样的值返回
  • Promise.all
    • 若数组所有 promise 被兑现,则返回一个兑现状态 promise + 数组
    • 若数组任何 promise 被拒绝,则返回一个拒绝状态 promise + 拒绝原因
  • Promise.any [总是异步的] - 返回第一个兑现的 Promise
    • 若传入空数组,则返回的 promise 是 rejected 状态
    • 任何一个 promise 被敲定[兑现],则返回的 promise 异步兑现
    • 若传入的数组全是被拒绝的,则返回的 promise 异步拒绝 + 原因数组 // AggregateError: No Promise in Promise.any was resolved
  • Promise.allsettled - 返回一个兑现的 promise,并描述每个 promise 结果的数组

2、源码模拟实现

MyPromise.race = function (arr) {
    return new MyPromise((resolve, reject) => {
        arr.forEach(it => MyPromise.resolve(it).then(resolve).catch(reject)) // 更推荐,原因:在某些复杂的链式调用中,能更清晰地将成功和失败的处理逻辑分开,提高代码的可读性和可维护性
        // 之前的写法:arr.forEach(it => MyPromise.resolve(it).then(resolve, reject))
    });
};
MyPromise.all = function (arr) {
    return new MyPromise((resolve, reject) => {
        if (arr.length === 0) { resolve([]); return; } // arr 为空数组时,forEach 不会执行
        const results = new Array(arr.length);
        let completedCount = 0; // 计数器
        arr.forEach((it, index) => { // 遍历所有 promises
            MyPromise.resolve(it).then((res) => {
                results[index] = res;
                completedCount++;
                if (completedCount === arr.length) { resolve(results) } // 返回结果
            }).catch(reject);
        });
    });
};
MyPromise.allSettled = function (arr) {
    return new MyPromise((resolve) => {
        const results = [];
        let completedCount = 0;
        if (arr.length === 0) { resolve([]); return; } // arr 为空数组时
        arr.forEach((promise, index) => {
            MyPromise.resolve(promise).then((value) => {
                results[index] = { status: 'fulfilled', value };
            }).catch((reason) => {
                results[index] = { status: 'rejected', reason };
            }).finally(() => {
                completedCount++;
                if (completedCount === arr.length) { resolve(results) }  // 返回结果
            });
        });
    });
};
const getErr = (info) => new AggregateError(info, 'All promises were rejected');
MyPromise.any = function (arr) {
    return new MyPromise((resolve, reject) => {
        const errors = [];
        let count = 0;
        if (arr.length === 0) { reject(getErr([])); return; }
        arr.forEach((promise, index) => {
            MyPromise.resolve(promise).then(resolve).catch((error) => {
                errors[index] = error;
                count++;
                if (count === arr.length) { reject(getErr(errors));}
            });
        });
    });
};