async/await 本身不决定串行或并行,关键在于 promise 创建和 await 时机:串行是逐个 await 实现顺序执行,适合有依赖场景;并行是先启动全部异步操作再统一等待,适合独立任务。

async/await 本身不直接决定串行或并行,关键在于你如何组织 Promise(或 Task、Deferred)的创建和等待时机。串行是“一个接一个等”,并行是“一起发、一起等”。
串行执行:按顺序逐个 await
每个 await 都会暂停当前函数执行,直到前一个异步操作完成,再启动下一个。适合有依赖关系或需严格顺序的场景(比如依次保存配置、分步提交表单)。
- 用 for...of 或 for 循环配合 await:自然实现串行
- 避免用 forEach + async 回调:它不会阻塞循环,实际是并发发起所有请求
- 示例(JavaScript):
for (const item of items) {
await api.save(item); // 等这个完成,才进下一轮
}
并行执行:先批量发请求,再统一 await
先把所有异步操作启动(不 await),收集它们的 Promise(或 Task、Deferred),最后用 Promise.all / Task.WhenAll / awaitAll 等一次性等待全部完成。适合彼此独立、可同时进行的任务(如批量拉取用户头像、并行更新多个设置)。
- Promise.all()(JS):任一失败则整体拒绝,返回数组结果
- Task.WhenAll()(C#):类似 Promise.all,支持泛型任务集合
- async { deferred1.await(); deferred2.await() }(Kotlin):先 async 启动,再分别 await —— 这才是并行;若写成 async{}.await() 连写,则退化为串行
注意错误处理与边界情况
并行时出错策略更复杂:Promise.all 默认“全败才停”,而 Promise.allSettled 会等全部结束并返回每个状态;C# 的 Task.WhenAll 抛 AggregateException,需展开 InnerExceptions;Kotlin 中 try/catch 包住 await 调用可捕获单个失败。
- 资源竞争:并行写同一数据库记录或共享变量时,可能引发冲突,需加锁或改回串行
- 限流控制:大量并行请求可能压垮服务,可用 p-limit(JS)、Semaphore(Kotlin/C#)限制并发数
- 超时保护:给单个请求或整个并行组加 timeout,避免无限等待
选择依据:看任务间是否相互依赖
如果后一步需要前一步的结果(如 token → 用 token 请求数据 → 用数据提交订单),必须串行;如果只是“把这 10 个用户的状态都更新一下”,彼此无关,就该并行。
不复杂但容易忽略











