创见博客
手写实现Promise.all
七崽爱吃小饼干2026/03/02阅读 1

手写实现 Promise.all

手动实现一个**Promise.all**,它的核心作用是:接收一个 Promise 数组,全部成功才返回成功结果,有一个失败就立即返回失败。

一、先明确 Promise.all 规则

  1. 接收一个可迭代对象(通常是数组),每个元素是 Promise / 普通值
  2. 所有 Promise 都成功 → 返回 按顺序的结果数组
  3. 只要 有一个失败 → 立即 返回该失败原因(短路机制)
  4. 空数组 → 直接返回成功空数组
  5. 普通值会被当成 已成功的 Promise

二、手写实现代码

javascript
/**
 * 手写 Promise.all
 * @param {Array} promises - Promise 数组
 * @returns {Promise} 最终结果
 */
function PromiseAll(promises) {
  // 1. 返回一个新的 Promise
  return new Promise((resolve, reject) => {
    // 转成数组(处理可迭代对象)
    const promiseArray = Array.from(promises);
    const length = promiseArray.length;
    // 存放最终结果
    const result = [];
    // 记录已完成的数量
    let count = 0;

    // 空数组直接 resolve
    if (length === 0) {
      resolve(result);
      return;
    }

    // 遍历所有 promise
    promiseArray.forEach((item, index) => {
      // 用 Promise.resolve 包装,兼容普通值
      Promise.resolve(item)
        .then((res) => {
          // **按顺序存放结果**
          result[index] = res;
          count++;

          // 全部完成才 resolve
          if (count === length) {
            resolve(result);
          }
        })
        .catch((err) => {
          // 只要一个失败,立即 reject
          reject(err);
        });
    });
  });
}

三、核心关键点

1. 为什么要用 result[index] = res?

因为 Promise 是异步的,执行速度不一样,必须按传入顺序存结果,不能用 push。

2. 为什么用 count 计数?

因为结果是按索引放的,result.length 不准,必须用计数器判断是否全部完成。

3. 为什么用 Promise.resolve(item)?

兼容普通值(数字、字符串、对象等),把它们变成已决议的 Promise。

4. 失败机制

只要有一个 reject,立刻触发整体失败,不等待其他 Promise。


四、测试使用

javascript
// 测试 1:全部成功
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 1000));
const p3 = 3;

PromiseAll([p1, p2, p3]).then(res => {
  console.log(res); // [1, 2, 3]
});

// 测试 2:有一个失败
const p4 = Promise.reject("出错啦");
PromiseAll([p1, p4, p2]).catch(err => {
  console.log(err); // "出错啦"
});

五、总结

  1. 返回新 Promise
  2. 遍历所有任务,用 Promise.resolve 包装
  3. 按索引存结果,保证顺序
  4. 计数器判断是否全部完成
  5. 任一失败立即 reject
评论
0/100