普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月21日首页

面试官:手写一个Promise.all

作者 spaceTop
2026年1月21日 09:34

Promise.all是javascript中处理并发请求的核心方法。面试中经常会被要求手写实现,以考察对Promise状态机、异步处理以及数组遍历的掌握。

1. Promise.all的核心逻辑

在动手写之前,先明确它的四个特性:

  1. 输入:接收一个可迭代对象(通常是数组)

  2. 返回:返回一个新的Promise

  3. 成功条件:只有当数组中所有的Promise都成功时,新Promise才成功,并返回一个按顺序排列的结果数组

  4. 失败条件: 只要有一个Promise失败,新Promise立即失败(Fail-fase),并返回第一个失败的错误

2. 手写代码实现

    /**
 * 实现Promise.all
 * 
 * @param promises 一个包含多个Promise实例的数组
 * @returns 一个新的Promise实例,只有当所有传入的Promise实例都成功时才会成功,否则会在第一个失败的Promise实例时失败
 */ 


function promissAll<T>(promises: Promise<T>[]): Promise<T[]> {
    // 1. 返回一个新的Promise实例
    return new Promise<T[]>((resolve, reject) => {
        // 判断参数是否为可迭代对象
        if (!promises || typeof promises[Symbol.iterator] !== 'function') {
            return reject(new TypeError('Argument is not iterable'));
        }
        const results: T[] = []; // 用于存储每个Promise的结果
        let completedCount = 0; // 用于跟踪已完成的Promise数量
        const promiseArray = Array.from(promises); // 将传入的参数转换为数组
        const total = promiseArray.length; // 获取Promise的总数量

        // 2. 如果传入的数组为空,立即resolve一个空数组
        if (total === 0) {
            resolve(results);
            return;
        }
        // 3. 遍历每个Promise实例
        promiseArray.forEach((promise, index) => {
            // 使用Promise.resolve确保即使传入的不是Promise实例也能正确处理
            Promise.resolve(promise).then((value) => {
                results[index] = value; // 存储当前Promise的结果
                completedCount++;
                // 如果所有Promise都已完成,resolve最终结果数组
                if (completedCount === total) {
                    resolve(results);
                }
            })
        }, (error) => {
            // 4. 如果有任何一个Promise实例失败,立即reject
            reject(error);
        })
    })

}

3. 实现细节解析

Q1: 为什么不直接result.push(value)?

  • 原因: Promise是异步的,执行完成的时间不确定。如果使用push,返回的结果顺序会乱,必须通过result[index]确保结果和原数组的索引一一对应。 Q2: 为什么用count计数而不是判断result.length?
  • 原因:在javascript数组中,如果你先复制了索引为2的值result[2] = 'a', 此时result.length 会直接变成3,但索引0和1可能还没完成。所以必须使用独立的计数器。

Q3: Promise.resolve(promise)的作用?

  • 请在评论区回复

Q4: Fail-fase(快速失败)机制

  • 一旦其中一个Promise触发了reject,新的Promise的状态就会变成rejected。由于Promise的状态只能改变一次,后续Promise成功的调用会被忽略

4. 测试用例

const p1 = Promise.resolve(1);
const p2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const p3 = 3; // 普通值

promissAll([p1, p2, p3])
  .then(res => console.log('成功:', res)) // 1秒后输出: 成功: [1, 2, 3]
  .catch(err => console.log('失败:', err));

const p4 = Promise.reject('报错了');
promissAll([p1, p4, p2])
  .then(res => console.log(res))
  .catch(err => console.log('失败:', err)); // 立即输出: 失败: 报错了

5. 总结

手写 Promise.all 的口诀:返回一个新 Promise,遍历数组看结果,索引对应存数据,全部成功才 resolve,一个失败就 reject。

细心的老铁肯定也看到了,Q3没有输出答案,欢迎各位大牛在评论区留言

❌
❌