关键在于按需选择执行方式:需串行用for...of,独立并行用promise.all,链式依赖用reduce,禁用foreach处理异步。

在 async/await 场景下处理数组中的异步任务,关键在于明确需求:是否需要按顺序执行(串行),还是允许并发执行(并行)。不同方式性能和行为差异明显,选错会导致请求乱序、错误掩盖或资源浪费。
需要串行执行:用 for...of 最直接
当任务有依赖关系(比如后一个请求要前一个返回的 ID)、或需严格控制节奏(防接口限流)、或调试时需逐个观察,就该用 for...of。它天然支持 await,代码线性清晰,出错位置一目了然。
- 写法简单:
for (const item of list) { const res = await api(item); } - 自动等待每个 Promise 完成后再进入下一轮循环
- 可配合 try/catch 在循环内单独捕获某次失败,不影响后续
需要并行执行:用 Promise.all 最高效
若所有请求彼此独立,且你只关心“全部完成”这个结果(比如批量拉取用户头像),Promise.all 是首选。它会同时发起所有请求,总耗时≈最慢那个请求的时间。
- 写法:
const results = await Promise.all(list.map(item => api(item))); - 注意:任一请求失败,整个 Promise.all 就 reject,如需容错,可搭配
.catch()或改用Promise.allSettled - 避免无节制并发——大量请求可能触发浏览器限制或服务端熔断,必要时加并发数控制(如 p-limit 库)
想手动控制流程:reduce 适合链式串行
当你需要把上一次的结果传给下一次调用(比如累计更新状态、构建依赖链),reduce 比 for...of 更灵活。它用 Promise 链把每次调用“缝”在一起。
- 核心逻辑是:
acc.then(() => api(nextItem)),让每个请求等前一个 resolve 后才开始 - 适合实现“逐个提交表单并收集响应 ID”的场景
- 相比 for...of,它更函数式,但可读性稍低,调试时堆栈略深
不推荐在循环中用 forEach
forEach 的回调函数默认不是 async,即使内部写 await,外部循环也不会等待。结果就是所有请求几乎同时发出,变成事实上的并行,但又无法用 Promise.all 统一收口——既失去串行可控性,又丢掉并行的可管理性。
- 常见误写:
list.forEach(async item => await api(item));—— 这段代码不会按顺序执行,也难以捕获错误 - 如果真要用,必须配合其他手段(如 map + Promise.all),否则不如换 for...of











