标准答案

  1. 入参通常是可迭代对象,元素可以是 Promise,也可以是普通值。
  2. 返回值必须是一个新的 Promise。
  3. 所有任务要并发开始,不能一个完成后再启动下一个。
  4. 结果数组必须按入参顺序保存,不能按完成顺序 push。
  5. 任意一个任务 reject 时外层立即 reject;入参为空时立即 resolve 空数组。

题目解析

这道题最容易写错的是“并发”和“顺序”同时存在。任务完成顺序不确定,但结果要按输入顺序返回,所以每个任务都要记录自己的 index。

Promise.all 不是错误收集工具。它的失败策略是快速失败,只要有一个失败,外层就进入 rejected;需要保留所有结果时应考虑 allSettled。

普通值也要兼容。用 Promise.resolve(item) 包一层,可以让 Promise、thenable 和普通值走同一套处理流程。

代码示例

下面的实现覆盖空数组、普通值、结果顺序和快速失败。

JavaScript
function promiseAll(items) {
  return new Promise((resolve, reject) => {
    const list = Array.from(items)
    const results = []
    let fulfilledCount = 0

    if (list.length === 0) {
      resolve([])
      return
    }

    list.forEach((item, index) => {
      Promise.resolve(item).then(
        value => {
          results[index] = value
          fulfilledCount += 1

          if (fulfilledCount === list.length) {
            resolve(results)
          }
        },
        reject
      )
    })
  })
}

常见误区

  • 用 results.push(value) 保存结果,导致返回顺序变成完成顺序。
  • 忘记处理空数组,外层 Promise 一直 pending。
  • 把任务写成串行 await,失去 Promise.all 的并发语义。

作者信息