实现 Promise.allSettled

手写实现 Promise.allSettled 方法,等待所有 Promise 完成(无论成功或失败)并返回结果数组

问题

Promise.allSettled 是 ES2020 引入的方法,它接收一个 Promise 数组作为参数,等待所有 Promise 都完成(无论成功或失败),然后返回一个新的 Promise,该 Promise 的结果是一个数组,包含每个 Promise 的状态和结果。

Promise.all 不同的是:

  • Promise.all 在任何一个 Promise 失败时就会立即 reject
  • Promise.allSettled 会等待所有 Promise 完成,不会因为某个 Promise 失败而提前结束

解答

/**
 * 实现 Promise.allSettled
 * @param {Array} promises - Promise 数组
 * @returns {Promise} 返回一个新的 Promise
 */
Promise.myAllSettled = function(promises) {
  // 参数校验:确保传入的是可迭代对象
  if (!Array.isArray(promises)) {
    return Promise.reject(new TypeError('参数必须是一个数组'));
  }

  return new Promise((resolve) => {
    const results = []; // 存储所有结果
    let completedCount = 0; // 已完成的 Promise 数量
    const total = promises.length;

    // 处理空数组的情况
    if (total === 0) {
      resolve(results);
      return;
    }

    // 遍历所有 Promise
    promises.forEach((promise, index) => {
      // 使用 Promise.resolve 包装,确保处理非 Promise 值
      Promise.resolve(promise)
        .then(value => {
          // Promise 成功时的处理
          results[index] = {
            status: 'fulfilled',
            value: value
          };
        })
        .catch(reason => {
          // Promise 失败时的处理
          results[index] = {
            status: 'rejected',
            reason: reason
          };
        })
        .finally(() => {
          // 无论成功或失败,都增加计数
          completedCount++;
          
          // 当所有 Promise 都完成时,resolve 结果数组
          if (completedCount === total) {
            resolve(results);
          }
        });
    });
  });
};

使用示例

// 示例 1:混合成功和失败的 Promise
const promise1 = Promise.resolve(100);
const promise2 = Promise.reject('错误信息');
const promise3 = new Promise((resolve) => setTimeout(() => resolve('延迟结果'), 1000));
const promise4 = Promise.resolve(200);

Promise.myAllSettled([promise1, promise2, promise3, promise4])
  .then(results => {
    console.log(results);
    // 输出:
    // [
    //   { status: 'fulfilled', value: 100 },
    //   { status: 'rejected', reason: '错误信息' },
    //   { status: 'fulfilled', value: '延迟结果' },
    //   { status: 'fulfilled', value: 200 }
    // ]
  });

// 示例 2:包含非 Promise 值
Promise.myAllSettled([1, 2, Promise.resolve(3), Promise.reject(4)])
  .then(results => {
    console.log(results);
    // 输出:
    // [
    //   { status: 'fulfilled', value: 1 },
    //   { status: 'fulfilled', value: 2 },
    //   { status: 'fulfilled', value: 3 },
    //   { status: 'rejected', reason: 4 }
    // ]
  });

// 示例 3:空数组
Promise.myAllSettled([])
  .then(results => {
    console.log(results); // 输出:[]
  });

// 示例 4:实际应用场景 - 批量请求
const urls = [
  'https://api.example.com/user/1',
  'https://api.example.com/user/2',
  'https://api.example.com/user/3'
];

const requests = urls.map(url => fetch(url).then(res => res.json()));

Promise.myAllSettled(requests)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`请求 ${index + 1} 成功:`, result.value);
      } else {
        console.log(`请求 ${index + 1} 失败:`, result.reason);
      }
    });
  });

关键点

  • 返回值结构统一:无论 Promise 成功或失败,都返回包含 status 字段的对象,成功时包含 value,失败时包含 reason

  • 使用 Promise.resolve 包装:通过 Promise.resolve(promise) 确保能够处理非 Promise 值(如普通数值、字符串等)

  • 保持结果顺序:使用 results[index] 而不是 results.push(),确保结果数组的顺序与输入数组一致

  • 计数器机制:使用 completedCount 统计已完成的 Promise 数量,当等于总数时才 resolve

  • 永远 resolvePromise.allSettled 返回的 Promise 永远不会 reject,即使所有输入的 Promise 都失败了

  • 边界情况处理:需要处理空数组的情况,直接返回空结果数组

  • 使用 finally:利用 finally 方法统一处理成功和失败后的计数逻辑,代码更简洁