likes
comments
collection
share

Promise的并发性方法汇总(包括手写实现)

作者站长头像
站长
· 阅读数 15

引言

本文将介绍Promise提供的4个静态方法Promise.all、Promise.allSettled、Promise.any和Promise.race,用于对多个Promise对象进行并发处理。所谓并发处理,就是指同时执行多个异步任务,并根据不同的需求获取它们的结果。

Promise.all

Promise.all(iterable) 入参 iterable 接受一个可迭代对象作为入参。返回值为一个待定 (pending) 的 promise 对象,根据入参不同分为两种情况:

  • 入参为一个空的可迭代对象:返回一个已完成状态的 promisefulfilled value 为这个空的可迭代对象;
  • 入参为一个非空的可迭代对象:
    • 在可迭代对象中所有元素都兑现时(若可迭代对象中有非 promise 则直接将其作为兑现结果),返回的 promise 状态会由 pending 切换为 fulfilled,并将所有结果组成的一个数组作为 fulfilled value(用 then 接收);
    • 若其中有任何一个 promise 被拒绝时,返回的 promise 状态会由 pending 切换为 fulfilled,并直接报出 rejected reason(用catch 捕获)。

手写 Promise.all

function all_(promises) {
    return new Promise((resolve, reject) => {
        const { length } = promises;
        // 空的可迭代对象则直接 resolve
        if (!length) return resolve(promises);
        const rsts = [];
        let count = 0;
        for (let i = 0; i < length; i++) {
            // Promise.resolve() 方法来兼容处理 promise 和非 promise
            Promise.resolve(promises[i]).then((e) => {
                rsts[i] = e;
                if (++count >= length) {
                    resolve(rsts);
                }
            }, reject);
        }
    });
}

Promise.allSettled

Promise.allSettled(iterable) 入参 iterable 接受一个可迭代对象作为入参。与 Promise.all 差不多,返回值为一个待定 (pending) 的 promise 对象,根据入参不同分为两种情况:

  • 入参为一个空的可迭代对象:返回一个已完成状态的 Promise,fulfilled value 为这个空的可迭代对象;(与 Promise.all 处理方式一致)
  • 入参为一个非空的可迭代对象:返回的 promise 会在可迭代对象中所有元素都兑现或被拒绝时(若可迭代对象中有非 promise 则直接将其作为兑现结果),将所有的处理结果(一个对象,包含 fulfilled or rejected 的状态)组成一个数组作为 fulfilled value(用 then 接收)。
    • 返回的兑现结果 PromiseAllSettledFulfilledValue (笔者自定义的一个类型,为方便解释)的类型如下:
type PromiseAllSettledFulfilledValue = { 
    status: 'fulfilled' | 'rejected'; // 处理结果状态 
    value?: unknown; // fulfilled value
    reason?: unknown; // rejected reason
}[];

手写 Promise.allSettled

function allSettled_(promises) {
    return new Promise((resolve) => {
        const { length } = promises;
        if (!length) return resolve(promises);
        const rsts = [];
        let count = 0;
        const resolveRsts = (i: number, e: { status: 'fulfilled' | 'rejected'; value?: unknown; reason?: unknown }) => {
            rsts[i] = e;
            if (++count >= length) {
                resolve(rsts);
            }
        };
        for (let i = 0; i < length; i++) {
            Promise.resolve(promises[i]).then(
                (e) =>
                    resolveRsts(i, {
                        status: 'fulfilled',
                        value: e,
                    }),
                (e) =>
                    resolveRsts(i, {
                        status: 'rejected',
                        reason: e,
                    })
            );
        }
    });
}

Promise.any

Promise.any(iterable) 入参 iterable 接受一个可迭代对象作为入参。与 Promise.all 类似,返回值为一个待定 (pending) 的 promise 对象,根据入参不同分为两种情况:

  • 入参为一个空的可迭代对象:返回一个已经被拒的 promiserejected reason 为这个空的可迭代对象;(注意这里与 Promise.all 处理方式的区别,这里是 reject
  • 入参为一个非空的可迭代对象:
    • 在可迭代对象中的任意一个元素兑现时(若可迭代对象中有非 promise 则直接将其作为兑现结果),返回的 promise 状态会由 pending 切换为 fulfilled,并直接将该结果作为 fulfilled value(用 then 接收);
    • 若所有 promise 都被拒绝了,返回的 promise 状态会由 pending 切换为 rejected,并用一个 AggregateError 实例来作为它的 rejected reason(用 catch 接收)。
interface AggregateError { 
    message: string; // 错误消息
    name: 'AggregateError'; // 错误名称
    errors?: unknown[]; // 错误列表
}[];

手写 Promise.any

function any_(promises) {
    return new Promise((resolve, reject) => {
        const { length } = promises;
        if (!length) return reject(new AggregateError(promises, 'All promises were rejected'));
        const reasons = [];
        let count = 0;
        for (let i = 0; i < length; i++) {
            Promise.resolve(promises[i]).then(resolve, (e) => {
                reasons[i] = e;
                if (++count >= length) {
                    reject(new AggregateError(reasons, 'All promises were rejected'));
                }
            });
        }
    });
}

Promise.race

Promise.race(iterable) 入参 iterable 接受一个可迭代对象作为入参,返回值为一个待定 (pending) 的 promise 对象,只要给定的迭代对象中的一个元素(若可迭代对象中有非 promise 则直接将其作为兑现结果)兑现或被拒绝,就采用第一个兑现或拒绝的元素的处理结果来 resolvereject。(若入参为空迭代对象,则表示没有元素兑现或被拒绝,返回值 promise 对象将一直处于 pending 状态。)

手写 Promise.race

function race_(promises) {
    return new Promise((resolve, reject) => {
        for (const p of promises) {
            Promise.resolve(p).then(resolve, reject);
        }
    });
}

全部代码及对比测试

解释:以下代码片段中将上述手写函数添加至 Promise 的静态方法中,并与原始方法进行对比测试,输出结果均为一致。

总结

Promise的并发性方法常用于处理多个promise的并发执行问题,每个方法应用场景大致总结如下:

  • Promise.all:用于接收多个promise兑现的结果,若其中有reject的会被reject
  • Promise.allSettled:用于接收多个promise兑现/拒绝的结果
  • Promise.any:用于接收多个promise中兑现最快的那个的兑现结果,若全部reject则reject
  • Promise.race:用于接收多个promise中兑现/拒绝最快的那个的处理结果