asyncio.as_completed返回按完成顺序排列的task对象异步迭代器,需调用task.result()或await task获取结果,异常需手动捕获;与gather不同,它不保证原始索引且无return_exceptions参数。

asyncio.as_completed 返回的是完成任务的迭代器,不是结果本身
调用 asyncio.as_completed() 后拿到的是一个 async for 可遍历的异步迭代器,每次 await 返回的是一个已完成的 asyncio.Task 对象,不是它的返回值。必须显式调用 .result() 才能取到实际数据。
- 常见错误:直接把
as_completed当作“结果流”用,比如for r in await as_completed(...)—— 这会报错,因为它是可等待对象,不是可迭代对象 - 正确姿势是
async for task in as_completed(...): result = await task或result = task.result() - 如果任务抛出异常,
task.result()会重新抛出;想忽略失败任务,需包try/except
如何安全地按完成顺序收集结果并避免阻塞
多数人想边完成边处理(比如写日志、发通知、存数据库),而不是等全部结束再统一取。这时候不能简单用 list() 包住 as_completed,否则失去“流式消费”意义。
- 推荐结构:
async for task in asyncio.as_completed(tasks): try: data = task.result() # 处理 data except Exception as e: # 记录异常,不中断循环 continue - 注意:所有
tasks必须已通过asyncio.create_task()或asyncio.ensure_future()提交到事件循环,不能传协程对象(否则as_completed会静默吞掉) - 若任务间耗时差异大,且后续处理较重(如 IO),建议把处理逻辑也设为
async并await,否则会阻塞其他任务结果的获取
as_completed 和 asyncio.gather 的关键区别在哪
asyncio.gather() 是按调用顺序返回结果,不管谁先完成;as_completed() 才真正按完成时间排序。但代价是:你无法知道某个结果对应原始任务的索引或参数。
- 如果你需要“完成顺序 + 原始上下文”,得自己绑定标识,例如:
tasks = [asyncio.create_task(fetch(url, i)) for i, url in enumerate(urls)],然后在fetch中返回(i, data) -
gather()支持return_exceptions=True,而as_completed没有等价参数——异常必须手动捕获 - 性能上无本质差异,但
as_completed在部分任务失败后仍能继续 yield 其他成功任务,gather默认遇到第一个异常就中断(除非开启return_exceptions)
容易被忽略的细节:事件循环状态和任务生命周期
as_completed 不会自动清理任务,也不保证任务一定执行完毕才 yield。如果任务在 yield 后、.result() 前被取消,task.result() 会抛 CancelledError。
- 确保所有任务都处于 pending 或 done 状态再传入
as_completed;已 cancelled 的任务会被立即 yield,但调.result()就崩 - 不要在
as_completed循环里重复await task多次——它可能已被消费过,Task对象不可重用 - 如果主协程提前退出(比如被
asyncio.wait_for中断),未完成的任务不会自动 cancel,得手动管理
真正麻烦的不是怎么写这三行代码,而是搞清哪个环节该 hold 住异常、哪个地方该保留上下文、以及什么时候该主动 cancel 掉慢任务。这些边界情况不写进日志,线上就只能靠猜。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











