# 手写Promise.all和Promise.race,面试再也不怕了

Promise是JS异步编程的核心,Promise.all和Promise.race更是面试常考的手写题。

今天从零实现这两个方法,顺便理解Promise的设计哲学。

Promise.all:全部成功才成功

Promise.all接收一个Promise数组,所有都fulfilled才返回结果数组,任意一个rejected就失败。

javascript 复制代码
// 用法
const results = await Promise.all([
    fetchUser(1),
    fetchOrders(1),
    fetchConfig()
]);
// results = [user, orders, config]

手写实现:

javascript 复制代码
function promiseAll(promises) {
    return new Promise((resolve, reject) => {
        // 不是数组,报错
        if (!Array.isArray(promises)) {
            return reject(new TypeError('Argument must be an array'));
        }

        const results = [];
        let resolvedCount = 0;
        const total = promises.length;

        // 空数组直接resolve
        if (total === 0) {
            return resolve([]);
        }

        promises.forEach((promise, index) => {
            // 包装非Promise值
            Promise.resolve(promise).then(
                (value) => {
                    results[index] = value; // 注意:用index保证顺序
                    resolvedCount++;

                    if (resolvedCount === total) {
                        resolve(results);
                    }
                },
                (reason) => {
                    reject(reason); // 任意一个失败就reject
                }
            );
        });
    });
}

// 测试
promiseAll([
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3)
]).then(console.log); // [1, 2, 3]

promiseAll([
    Promise.resolve(1),
    Promise.reject('error'),
    Promise.resolve(3)
]).catch(console.log); // 'error'

关键点:

  1. 结果数组的顺序和输入顺序一致(不是按完成顺序)
  2. 任意一个rejected就立即reject
  3. 非Promise值会被Promise.resolve包装

Promise.race:谁快用谁

Promise.race返回最先完成的那个Promise的结果,不管成功还是失败。

javascript 复制代码
// 用法:超时控制
const result = await Promise.race([
    fetchData(),
    new Promise((_, reject) =>
        setTimeout(() => reject(new Error('timeout')), 5000)
    )
]);

手写实现:

javascript 复制代码
function promiseRace(promises) {
    return new Promise((resolve, reject) => {
        for (const promise of promises) {
            Promise.resolve(promise).then(
                (value) => resolve(value),  // 第一个完成的直接resolve
                (reason) => reject(reason)  // 第一个失败的直接reject
            );
        }
    });
}

// 测试:超时控制
function fetchWithTimeout(url, timeout = 5000) {
    return Promise.race([
        fetch(url),
        new Promise((_, reject) =>
            setTimeout(() => reject(new Error('请求超时')), timeout)
        )
    ]);
}

Promise.allSettled:等全部完成

Promise.allSettled等所有Promise都完成(不管成败),返回每个的状态和结果。

javascript 复制代码
const results = await Promise.allSettled([
    Promise.resolve('成功'),
    Promise.reject('失败'),
    Promise.resolve('又成功')
]);

// [
//   { status: 'fulfilled', value: '成功' },
//   { status: 'rejected', reason: '失败' },
//   { status: 'fulfilled', value: '又成功' }
// ]

手写实现:

javascript 复制代码
function promiseAllSettled(promises) {
    return new Promise((resolve) => {
        const results = [];
        let completed = 0;
        const total = promises.length;

        if (total === 0) return resolve([]);

        promises.forEach((promise, index) => {
            Promise.resolve(promise).then(
                (value) => {
                    results[index] = { status: 'fulfilled', value };
                },
                (reason) => {
                    results[index] = { status: 'rejected', reason };
                }
            ).finally(() => {
                completed++;
                if (completed === total) {
                    resolve(results);
                }
            });
        });
    });
}

Promise.any:第一个成功的

Promise.any返回第一个成功的Promise,全部失败才reject。

javascript 复制代码
function promiseAny(promises) {
    return new Promise((resolve, reject) => {
        const errors = [];
        let rejectedCount = 0;
        const total = promises.length;

        if (total === 0) {
            return reject(new AggregateError([], 'All promises were rejected'));
        }

        promises.forEach((promise, index) => {
            Promise.resolve(promise).then(
                (value) => resolve(value),
                (reason) => {
                    errors[index] = reason;
                    rejectedCount++;
                    if (rejectedCount === total) {
                        reject(new AggregateError(errors, 'All promises were rejected'));
                    }
                }
            );
        });
    });
}

四个方法对比

方法 成功条件 失败条件 返回
Promise.all 全部成功 任意一个失败 结果数组
Promise.race 第一个完成 第一个失败 单个结果
Promise.allSettled 全部完成(不管成败) 不会失败 状态+结果数组
Promise.any 任意一个成功 全部失败 单个结果

手写Promise系列是面试高频题,核心就是理解Promise的构造和状态管理。把这几个实现搞懂,Promise就不再是难点了。

建议自己在编辑器里写一遍,比看十遍都管用。

相关推荐
八月的雨季 最後的冰吻1 小时前
C++ 面试要点笔记(续)
c++·笔记·面试
用户84913717547162 小时前
【技术面试高频】MySQL 存储 IP 地址:你还在用 VARCHAR?
mysql·面试
Rain的Java大神之路3 小时前
Java 三层架构,90% 的人只答对一半:Controller 不是瘦,Service 不是透传,Dao 不是只写 SQL
java·后端·面试
怕浪猫3 小时前
前端转全栈最低成本的技术栈选择
前端·javascript·面试
考研保研资料分享4 小时前
2022护理保研浙江大学:湘雅PICOS追问、健康宣教口译与科研准备
面试
程序员Sunday13 小时前
TypeScript 的 any、unknown、never 有什么区别?
前端·面试·typescript
小 黄 鸡14 小时前
HashMap 面试必会知识点详解(JDK 8)
java·面试·职场和发展
程序员Sunday18 小时前
ES Module 和 CommonJS 有什么区别?循环依赖时会发生什么?
前端·javascript·面试
溯原的技术笔记19 小时前
JavaScript 闭包
面试