实现 Promise.all

基础知识

Promise.allPromise 构造函数上的一个静态方法,它接收一个可迭代对象(通常是 Promise 数组)作为参数,并返回一个新的 Promise。这个方法是并行处理多个异步操作的强大工具。

要实现 Promise.all,我们必须理解其核心的行为特性:

  1. 并行执行: Promise.all 会等待所有传入的 Promise 都达到最终状态(fulfilledrejected)。它允许所有异步操作并行开始,而不是一个接一个地等待。

  2. “全成则成” (All Success):

    • 只有当所有传入的 Promise 都成功(fulfilled)时,由 Promise.all 返回的新 Promise 才会成功。
    • 成功时,其 resolve 的值是一个数组,包含了所有传入 Promise 的成功值,并且顺序与传入的 Promise 数组的顺序严格一致。
  3. “一败俱败” (One Failure):

    • 只要传入的 Promise 中有任何一个失败(rejected),由 Promise.all 返回的新 Promise 就会立即失败。
    • 失败时,其 reject 的原因是第一个失败的 Promise 的原因。
  4. 处理非 Promise 值: 如果传入的数组中包含非 Promise 的值(如数字、字符串、普通对象),Promise.all 会将它们视为已经成功(fulfilled)的 Promise,并将其值直接放入结果数组中。

核心思路

我们的目标是创建一个函数 myPromiseAll(promises),它能模拟原生 Promise.all 的行为。核心思路可以概括为“计数器与结果收集”

  1. 返回新 Promise: 函数必须返回一个新的 Promise,因为 Promise.all 本身就是一个异步过程。

  2. 初始化:

    • 创建一个数组 results,用于按顺序存放所有成功的结果。
    • 创建一个计数器 resolvedCount,用于记录已经成功的 Promise 数量。
  3. 处理空数组: 这是一个重要的边界情况。如果传入的数组为空,Promise.all 应立即以一个空数组成功返回。

  4. 遍历输入: 遍历传入的 promises 数组,对每个元素进行处理。使用索引 i 来确保结果能被放置在 results 数组的正确位置。

  5. 统一处理: 对数组中的每个元素(无论是 Promise 还是普通值),都使用 Promise.resolve() 进行包装。这可以确保我们后续处理的是一个统一的 Promise 对象,极大地简化了逻辑。

  6. 监听状态: 对每个包装后的 Promise 调用 .then() 方法,来监听它的成功与失败。

    • 成功时: 将成功的值存入 results 数组的对应索引位置,然后将 resolvedCount 加一。每次增加后,都检查 resolvedCount 是否等于总数。如果相等,说明所有 Promise 都已成功,此时 resolve 主 Promise 并传入 results 数组。
    • 失败时: 一旦监听到任何一个 Promise 失败,无需等待其他 Promise,立即 reject 主 Promise,并将失败原因传递出去。

代码实现

下面是一个遵循核心思路的 myPromiseAll 函数实现。

/**
 * 自定义实现 Promise.all
 * @param {Iterable<any>} promises - 一个可迭代对象,如 Promise 数组
 * @returns {Promise<Array<any>>} - 返回一个新的 Promise
 */
function myPromiseAll(promises) {
  // 1. 返回一个新的 Promise
  return new Promise((resolve, reject) => {
    // 2. 确保输入是数组(简化处理,原生支持所有可迭代对象)
    if (!Array.isArray(promises)) {
      return reject(new TypeError('Argument must be an array.'));
    }

    const promiseCount = promises.length;
    const results = new Array(promiseCount);
    let resolvedCount = 0;

    // 3. 处理传入数组为空的边界情况
    if (promiseCount === 0) {
      return resolve([]);
    }

    // 4. 遍历所有传入的 promise
    promises.forEach((item, index) => {
      // 5. 使用 Promise.resolve() 统一处理,兼容非 Promise 值
      Promise.resolve(item).then(
        // 6. 成功回调
        (value) => {
          results[index] = value; // 按顺序存放结果
          resolvedCount += 1; // 计数器加一

          // 如果所有 promise 都已成功,则 resolve 主 promise
          if (resolvedCount === promiseCount) {
            resolve(results);
          }
        },
        // 7. 失败回调
        (reason) => {
          // 一旦有任何一个 promise 失败,立即 reject 主 promise
          reject(reason);
        }
      );
    });
  });
}

代码解析

  1. 返回 Promise:函数主体为 return new Promise(...),遵循所有返回 Promise 函数的标准结构,通过 Promise 封装异步任务的最终状态。
  2. 初始化
    • results 数组用于按顺序存储每个任务的结果。
    • resolvedCount 计数器跟踪已完成的成功任务数。
    • promiseCount 记录总任务数,通过 new Array(promiseCount) 初始化固定长度数组,确保可直接通过索引赋值结果。
  3. 空数组处理:当 promiseCount === 0 时,立即返回 Promise.resolve([]),与原生 Promise.all 对空数组的处理一致。
  4. 遍历任务数组:使用 forEach 遍历输入数组,通过 index 参数保证结果数组顺序与输入一致,即使任务完成顺序不同。
  5. 统一转换为 Promise:通过 Promise.resolve(item) 处理每个任务:
    • item 是 Promise,直接返回该 Promise。
    • item 是普通值,返回 Promise.resolve(item) 包装的立即成功 Promise,确保后续处理的均为标准 Promise 对象。
  6. 成功回调处理
    • 子任务成功时,通过 results[index] = value 将结果存入对应索引位置。
    • resolvedCount++ 递增计数器,当 resolvedCount === promiseCount 时,说明所有任务成功,调用主 Promise 的 resolve(results) 返回完整结果数组。
  7. 失败回调处理:任意子任务失败时,立即调用主 Promise 的 reject(reason),终止后续处理,遵循 Promise.all “一败俱败”的原则,后续任务结果被忽略。
Copyright © Jun 2025 all right reserved,powered by Gitbook该文件修订时间: 2025-07-03 17:35:08

results matching ""

    No results matching ""

    results matching ""

      No results matching ""