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

asyncio.wait 返回的是两个集合,不是列表
很多人拿到 asyncio.wait 的返回值后直接用索引取元素(比如 done[0]),结果报 TypeError: 'set' object is not subscriptable。这是因为它的返回值是两个 set:一个装已完成的 Task 或 Future,另一个装还在跑的。
常见错误场景:想等几个协程完成,然后按顺序处理结果,却误以为返回值是有序列表。
-
asyncio.wait不保证完成顺序,也不保留原始传入顺序 - 返回的
done和pending都是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_COMPLETED、asyncio.FIRST_EXCEPTION、asyncio.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 里放的是 Task 或 Future 对象,不是协程返回值。直接 print 它们只会看到 <task finished name="..." result="..."></task> 这类字符串,而无法安全提取值——尤其当 task 出错时,不 await 就拿不到异常。
真实使用中常漏掉这步,导致后续逻辑拿不到数据或静默失败。
- 正确做法:遍历
done,对每个task调用task.result()(会抛出异常)或await task(更自然) - 如果 task 已经完成,
task.result()是安全的;但如果还没完成(理论上不该进 done,但并发边界下可能有误判),会报InvalidStateError - 推荐统一用
await task,既清晰又符合 async/await 语义










