标准答案
- 入参通常是可迭代对象,元素可以是 Promise,也可以是普通值。
- 返回值必须是一个新的 Promise。
- 所有任务要并发开始,不能一个完成后再启动下一个。
- 结果数组必须按入参顺序保存,不能按完成顺序 push。
- 任意一个任务 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 的并发语义。