asyncio.gather()适合要结果、要顺序、要省心的场景,直接返回按输入顺序排列的结果列表,默认任一异常即中断;asyncio.wait()适合要看状态、要控时机、要分成败的场景,返回done和pending集合,需手动处理结果与异常。

asyncio.gather() 适合“要结果、要顺序、要省心”
它直接返回按输入顺序排列的结果列表,比如 gather(coro_a(), coro_b()) 一定返回 ["a_result", "b_result"]。默认行为是任一协程抛异常就立刻 raise,不用手动检查——这符合多数批量调用的直觉:出错就停,不继续浪费资源。
常见错误现象:传入协程对象(非 Task)时出错?不会,gather() 内部自动调用 create_task(),你直接传 coro() 就行。
性能影响:所有任务共享同一个取消作用域;一旦某个失败且未设 return_exceptions=True,其余正在运行的任务大概率被取消——不是“优雅退出”,而是强制中断。
- 需要按位置取值(比如第 0 个结果必须是登录响应)→ 必须用
gather() - 想让失败任务也作为结果返回 → 加参数
return_exceptions=True - 没超时需求,只关心“全做完再统一处理” →
gather()最简洁
asyncio.wait() 适合“要看状态、要控时机、要分成败”
wait() 不返回结果,只返回两个集合:done(已完成的 Task)和 pending(还在跑的)。每个 Task 都得手动调 task.result() 或 task.exception() 才能拿到值或错误——这很啰嗦,但换来的是控制权。
立即学习“Python免费学习笔记(深入)”;
容易踩的坑:异常被“吞掉”。wait() 默认不传播异常,哪怕 coro_b() 抛了 ValueError,它照样进 done 集合,你不遍历检查 task.exception() 就完全不知道失败了。
使用场景明确:需要等第一个完成(return_when=asyncio.FIRST_COMPLETED)、等任意一个出错就退出、或配合 timeout 做超时后清理 pending 任务。
- 传协程前必须先包装成
Task:写成wait(create_task(coro_a()), create_task(coro_b())),否则报错 - 想实现“最多等 3 秒,超时就取消剩下任务” →
wait(..., timeout=3)+ 遍历pending调cancel() - 要区分哪些成功、哪些失败、哪些卡住 → 只能靠
wait()的done/pending分离能力
为什么不能混用?关键差异在语义层
它们根本不是同一类工具:gather() 是“结果收集器”,wait() 是“状态监视器”。选错就像拿温度计当螺丝刀——能凑合用,但拧不紧还容易滑丝。
典型误用:用 wait() 替代 gather() 只为“看起来更底层”,结果漏查异常、结果顺序混乱、代码反而更长;或者给 gather() 套 wait_for() 加超时,导致所有任务一起被 cancel,哪怕只有一个是慢请求。
参数差异明显:gather() 没有 return_when 和 timeout 参数;wait() 不支持直接传协程,也不保证结果顺序。
- 想等全部完成 + 按序拿结果 + 自动报错 →
gather() - 想等第一个完成 / 等第一个失败 / 超时后保留已完成结果 →
wait() - 需要同时知道“谁完了、谁挂了、谁还在跑” → 只有
wait()能做到
复杂点往往藏在异常处理和任务生命周期里
最常被忽略的不是语法,而是异常如何流转、任务何时被取消、以及 pending 里的 Task 是否还活着。比如 wait() 返回后,pending 里的 Task 如果没被显式 cancel(),它可能继续在后台运行,占用资源却不被感知。
又比如 gather(return_exceptions=True) 返回的列表里混着 Exception 实例和正常结果,你得自己 isinstance(x, Exception) 判断;而 wait() 的 done 里每个 Task 都得单独调 exception(),漏一个就漏一个错误。
真正难的不是写哪一行代码,是想清楚:你要的是“结果”,还是“状态”;你要的是“原子性”,还是“可中断性”。选对了,后面全是顺的;选错了,调试时连日志都找不到漏掉的异常在哪。


















