Skip to content

2795. 并行执行 Promise 以获取独有的结果

2795. 并行执行 Promise 以获取独有的结果

ts
type FulfilledObj = {
  status: 'fulfilled'
  value: string
}
type RejectedObj = {
  status: 'rejected'
  reason: string
}
type Obj = FulfilledObj | RejectedObj

function promiseAllSettled(functions: Function[]): Promise<Obj[]> {
  return new Promise((reslove) => {
    const result = Array.from({ length: functions.length })
    let endCount = 0

    for (let i = 0; i < functions.length; i++) {
      functions[i]()
        .then((value: string) => (result[i] = { status: 'fulfilled', value }))
        .catch((reason: string) => (result[i] = { status: 'rejected', reason }))
        .finally(() => {
          endCount += 1
          if (endCount === functions.length) {
            reslove(result as Obj[])
          }
        })
    }
  })
}

自实现 Promise.allSettled

ES2020 引入了原生的 Promise.allSettled,这里本质上是在手写一个等价实现。

核心机制:计数 + 按索引写入

result 数组的长度预分配为 functions.length,每个 Promise 的 then/catch 写入自己的索引位置。endCount 计数器在 finally 中自增——因为 finally 无论成功失败都会执行,所以不会漏计数。

endCount === functions.length 时,所有 Promise 都已 settled,resolve 整个结果数组。

为什么用 finally 计数

thencatch 各自处理成功/失败后写入结果,如果把计数也写在这两个分支里,代码会更分散。finally 正好在 then/catch 之后执行,且无条件执行,适合作为统一计数位置。

与 Promise.all 的关键差异

Promise.all 遇到第一个 reject 就立即短路,其他正在执行中的 Promise 的结果会被丢弃。而 allSettled 等待全部完成,返回每个 Promise 的完整状态(成功或失败)。

这类行为通常出现在“批量请求、部分失败不影响整体”的场景里,比如批量检查多个服务状态时,需要保留所有结果而不是在第一个失败后直接中断。

顺序保证

因为每个 Promise 写入 result[i] 是固定的(对应 functions 中的原始位置),无论 Promise 完成的先后顺序如何,最终结果数组中的顺序始终和 functions 中的顺序一致。