实现 Promise.all 方法

不使用 Promise.all 实现并发执行多个 Promise

问题

实现一个 myAll 方法,功能与 Promise.all 相同,但不能直接使用 Promise.all 语法。

解答

错误实现(串行执行)

async function myAll<T extends unknown[] | []>(
  values: T
): Promise<{ [P in keyof T]: Awaited<T[P]> }> {
  const arr = [];
  for (let i = 0; i < values.length; i++) {
    arr.push(await values[i]);
  }
  return arr as { [P in keyof T]: Awaited<T[P]> };
}

这个实现有问题:使用 await 在循环中会导致串行执行,失去了并发的优势。

正确实现(并发执行)

async function myAll<T extends unknown[] | []>(
  values: T
): Promise<{ [P in keyof T]: Awaited<T[P]> }> {
  return new Promise((resolve, reject) => {
    const results: any[] = [];
    let completed = 0;
    
    if (values.length === 0) {
      resolve(results as { [P in keyof T]: Awaited<T[P]> });
      return;
    }
    
    values.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(value => {
          results[index] = value;
          completed++;
          
          if (completed === values.length) {
            resolve(results as { [P in keyof T]: Awaited<T[P]> });
          }
        })
        .catch(reject);
    });
  });
}

使用示例

async function request(value: string) {
  await sleep(1000);
  return value;
}

// 三个请求并发执行,总耗时约 1 秒
const results = await myAll([
  request('a'),
  request('b'),
  request('c')
]);

关键点

  • 循环中使用 await 会导致串行执行,每个 Promise 依次等待
  • 正确做法是先启动所有 Promise,再统一等待结果
  • 使用计数器追踪完成数量,所有 Promise 完成后才 resolve
  • Promise.resolve() 包装值,确保非 Promise 值也能正常处理
  • 任何一个 Promise reject 时,整个 myAll 立即 reject