Python asyncio.wait 的返回结果分析

4次阅读

asyncio.wait 返回两个无序set而非列表,直接索引会报错;timeout仅控制等待时长而不终止任务;return_when决定返回时机但总返回完整(done, pending);done中对象需await才能获取结果。

Python asyncio.wait 的返回结果分析

asyncio.wait 返回的是两个集合,不是列表

很多人拿到 asyncio.wait 的返回值后直接用索引取元素(比如 done[0]),结果报 TypeError: 'set' Object is not subscriptable。这是因为它的返回值是两个 set:一个装已完成的 TaskFuture,另一个装还在跑的。

常见错误场景:想等几个协程完成,然后按顺序处理结果,却误以为返回值是有序列表。

  • asyncio.wait 不保证完成顺序,也不保留原始传入顺序
  • 返回的 donepending 都是 set,无序、去重、不支持下标访问
  • 如果需要顺序或结果映射,得自己用 asyncio.gather 或显式维护 task → result 关系

timeout 参数不等于“最多等多久”,而是“超时后立即返回”

设了 timeout=5,不代表所有任务都会被取消或中断;它只是让 wait 在 5 秒后不再阻塞,把当时还没完成的任务放进 pending 集合里,而已运行的任务继续执行——除非你手动取消它们。

典型踩坑:以为加了 timeout 就能防卡死,结果 pending 里的 task 还在后台跑,甚至引发资源泄漏。

立即学习Python免费学习笔记(深入)”;

  • timeout 只影响 wait 函数本身的阻塞行为,不影响 task 生命周期
  • 真正要中断任务,得配合 asyncio.create_task(...).cancel() 或用 asyncio.wait_for
  • 若需“超时即终止”,优先考虑 asyncio.wait_for,而不是靠 wait + 手动 cancel

return_when 控制的是“什么时候返回”,不是“返回哪些”

return_when 的三个可选值:asyncio.FIRST_COMPLETEDasyncio.FIRST_EXCEPTIONasyncio.ALL_COMPLETED,决定的是函数何时从等待中退出,但每次返回的都是完整的 (done, pending) 二元组。

容易混淆的点:以为设成 FIRST_COMPLETED 就只返回一个 done,其实 done 里可能有多个——只要它们恰好在同一轮事件循环中完成(比如多个 task 同时 resolve)。

  • FIRST_EXCEPTION 不会跳过正常完成的 task,只要有一个抛异常,就立刻返回,但 done 里仍包含其他已成功结束的 task
  • ALL_COMPLETED 是唯一能确保 pending 为空的情况(前提没 timeout)
  • 别依赖 len(done) == 1 来判断是否首次完成,这是竞态行为

done 集合里的对象必须 await 才能得到结果

done 里放的是 TaskFuture 对象,不是协程返回值。直接 print 它们只会看到 <task finished name="..." result="..."></task> 这类字符串,而无法安全提取值——尤其当 task 出错时,不 await 就拿不到异常。

真实使用中常漏掉这步,导致后续逻辑拿不到数据或静默失败。

  • 正确做法:遍历 done,对每个 task 调用 task.result()(会抛出异常)或 await task(更自然)
  • 如果 task 已经完成,task.result() 是安全的;但如果还没完成(理论上不该进 done,但并发边界下可能有误判),会报 InvalidStateError
  • 推荐统一用 await task,既清晰又符合 async/await 语义

事情说清了就结束。最麻烦的其实是 done/pending 的类型和生命周期管理——它们不像 gather 那样“自动收尾”,得你自己盯着状态、处理异常、清理 pending。

text=ZqhQzanResources