asyncio.gather直接返回结果列表,顺序与输入一致;asyncio.wait返回done和pending任务集合,需手动调用result()获取结果,支持超时、提前返回等精细控制。

asyncio.gather 返回结果,asyncio.wait 返回任务状态
这是最根本的差异:asyncio.gather 直接返回协程执行后的值列表,顺序与传入一致;asyncio.wait 返回两个集合:done(已完成的 Task 对象)和 pending(未完成的 Task),你需要手动调用 task.result() 才能拿到结果。
常见错误现象:
- 用
asyncio.wait后直接 print(done),看到的是Task对象,不是你想要的字符串或数字 - 对
pending中的 task 调用.result()会抛InvalidStateError(因为还没完成)
使用场景举例:
- 爬多个 URL 并汇总响应体 → 选
gather - 启动 10 个长耗时任务,只要第一个返回就中断其余 → 选
wait+return_when=asyncio.FIRST_COMPLETED
asyncio.wait 支持超时和返回策略,gather 不支持原生超时
asyncio.wait 的 timeout 参数是硬性截止时间(单位秒),超时后未完成的任务自动进入 pending 集合;而 gather 没有内置 timeout,必须靠 asyncio.wait_for 包裹单个协程,或整体用 asyncio.wait 控制。
参数差异要点:
-
asyncio.wait(tasks, timeout=2.0):2 秒后不管完成几个都返回 -
asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION):任一协程抛异常立即返回 -
asyncio.gather(*tasks, return_exceptions=True):仅控制异常是否传播,不改变等待行为
性能影响:
- 单纯等全部完成,两者底层调度开销接近
- 但
wait在超时或提前返回时能更快释放资源,gather会一直等到所有完成或被 cancel
gather 自动包装协程,wait 要求显式转为 Task
asyncio.gather 会把传入的协程对象(coro)自动用 asyncio.create_task 或 ensure_future 封装成 Task;而 asyncio.wait 虽然也能接收协程,但文档明确建议先转成 Task,否则在某些版本中行为不稳定(尤其涉及取消时)。
实操建议:
- 安全写法:
tasks = [asyncio.create_task(coro) for coro in coros],再传给wait - 避免直接传
[coro1(), coro2()]给wait,尤其当你要调用task.cancel()时 -
gather对此宽容得多,传协程、Task、Future 都能处理
gather 可取消且支持 return_exceptions,wait 的取消更底层
asyncio.gather 是少数能直接被 cancel() 的高层封装:它内部维护子任务引用,cancel 会逐个向子 Task 传递取消信号。配合 return_exceptions=True,即使部分失败也能拿到全部结果(含 CancelledError 或其他异常对象)。
而 wait 本身不提供 cancel 接口,你要取消,得自己遍历 done 和 pending 中的 Task 对象调用 .cancel()。
容易踩的坑:
- 用
gather但没设return_exceptions=True,一个协程异常会导致整个gather抛出该异常,其余结果丢失 - 用
wait后只处理done,忘了pending中可能还有活跃任务,导致资源泄漏 - 在
wait返回后对pending里的 task 不做后续 await 或 cancel,event loop 可能卡住
真正要注意的不是“哪个更好”,而是“你是否需要观察任务生命周期”。如果只拿结果,gather 更省心;一旦涉及超时、异常分流、动态取消或监控进度,wait 的显式状态管理就不可替代——这点很容易被忽略,直到线上出现超时堆积或任务滞留。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











