用 await 在 for 或 for...of 循环中顺序执行异步任务,需置于 async 函数内;避免 foreach 和提前创建 promise;错误需显式处理,兼顾依赖与性能。

用 await 在循环中实现异步任务的顺序执行,核心就一条:把 await 放在 for 或 for...of 循环体内,且整个循环必须写在 async 函数里。这样每次迭代都会等前一个异步操作完成,再进入下一轮。
必须用 for 或 for...of,别碰 forEach
forEach 会忽略 async 回调返回的 Promise,所有任务几乎同时发起,完全不等结果——这不是顺序,是并发假象。
- ✅ 正确:
for (const item of list) { await doAsync(item); } - ❌ 错误:
list.forEach(async item => await doAsync(item));(无等待效果) - ⚠️ 补充:for...in 不适合遍历数组,易出索引类型或原型链问题;传统
for (let i = 0; i 同样可靠,尤其需要索引时更直接
每个 await 都要配 try/catch
顺序执行下,一次失败默认中断整个流程。是否继续,取决于业务逻辑,但绝不能让错误静默吞掉。
- 单次出错想跳过?
try { await api(item) } catch (e) { console.warn('跳过', item, e); } - 某环节失败必须终止?直接抛出错误,或在 catch 中
throw e - 别把整个 for 包进一个 try/catch:那样一错全停,且无法定位具体哪一项失败
注意 Promise 创建时机,避免“伪并行”
如果提前把所有 Promise 都 new 出来再 await,它们其实已开始执行——哪怕你按顺序 await,实际仍是并发发起。
- ❌ 错误示例:
const promises = items.map(x => fetch(x)); for (const p of promises) await p;(fetch 已全部发出) - ✅ 正确做法:
for (const x of items) await fetch(x);(每次到这行才真正发请求) - 复杂逻辑可抽成独立 async 函数,但调用时仍需
await fn(item),不能只定义不调用
顺序 ≠ 必须串行,先想清楚要不要等
纯顺序执行性能代价明显:10 个 300ms 请求,串行要 3 秒;并行只要约 300ms。真需要顺序,通常是因为依赖关系。
- ✅ 必须串行:比如刷新 token 后才能用新 token 请求下一个接口
- ✅ 可并行+保序:任务彼此独立,但最终结果要按原顺序排列 → 用
Promise.allSettled()或Promise.all()+ 索引映射 - ⚠️ 折中方案:控制并发数(如最多同时 3 个),可用
p-limit库或手写分批逻辑











