asyncio.wait()超时后返回(done, pending)二元组,pending含未完成任务可手动取消;asyncio.as_completed()无内置timeout,强行加timeout易误解语义且破坏迭代契约。

asyncio.wait() 和 asyncio.as_completed() 在超时处理上的核心差异在于:前者在超时后返回的是“尚未完成的任务集合”,后者在超时后会抛出 TimeoutError(如果未配合异常捕获),且不直接提供“哪些任务还活着”的信息。
asyncio.wait() 的超时行为更可控
调用 asyncio.wait(tasks, timeout=5.0) 会在 5 秒后立即返回,不管任务是否完成。它返回一个二元组 (done, pending),其中 pending 是仍处于运行中、未被取消的任务集合(类型为 set)。你可以主动对 pending 中的任务调用 cancel(),或后续 await 它们(需注意已取消任务会抛 CancelledError)。
- 超时不是中断,只是“到点返回”,任务仍在事件循环中运行(除非你手动取消)
- 适合需要统一管理生命周期的场景,比如批量发起请求并统一超时+清理
- 若想实现“超时即终止”,必须显式遍历
pending并调用task.cancel()
asyncio.as_completed() 本身不支持 timeout 参数
asyncio.as_completed() 是一个异步生成器,按完成顺序产出结果。它**没有内置 timeout 参数**。若想加超时,必须在外层用 asyncio.wait_for() 包裹单个 as_completed 迭代,或者对整个迭代过程设限——但这会导致逻辑复杂且易出错。
- 常见误写:
await asyncio.wait_for(asyncio.as_completed(tasks), timeout=5)—— 这实际只等待“第一个结果产出”,而非所有任务完成 - 真正想等“最多 5 秒内所有能完成的结果”,需用
wait()配合done集合 + 手动 gather - 它的优势是流式消费,劣势是缺乏原生超时语义,超时控制需额外封装
超时后如何获取已完成结果?
用 wait() 可直接从 done 集合中提取结果(需用 task.result(),注意捕获异常);而 as_completed() 在超时中断时,已产出的结果不会丢失,但未产出的将无法再通过该生成器获取——除非你重新构造一个新的 as_completed 迭代器(此时任务可能已结束或已取消)。
-
wait():超时后,done中每个 task 调用.result()即可拿到值或异常 -
as_completed():若用wait_for包裹迭代,中断时已 yield 的结果仍有效,但后续迭代会因生成器被取消而不可继续 - 想兼顾“按完成顺序 + 超时总控”,推荐组合使用:
wait()获取done,再用asyncio.gather(*done, return_exceptions=True)整理结果
实际选型建议
需要明确区分“已完成”和“待处理”、要统一取消或重试、有严格总耗时限制 → 选 wait()。只需要尽快处理响应、不在乎总时长、任务彼此独立且失败可忽略 → as_completed() 更自然,但超时得靠外层逻辑兜底(如设置任务级 timeout 或用信号控制)。
- API 调用聚合、批处理、资源清理类场景,
wait()更稳妥 - 实时日志收集、监控探活、消息广播等“尽力而为”场景,
as_completed()更轻量 - 不要试图给
as_completed()“硬加 timeout”,容易误解语义和破坏迭代契约










