asyncio.as_completed返回异步迭代器,每次产出已完成的task对象而非结果本身,需显式await或调用result()获取值;常见错误是误用async for或未await导致卡在task状态。

asyncio.as_completed 返回的是什么?
它返回一个异步迭代器,每次 await 都产出一个已完成的 asyncio.Task 对象(不是结果本身),你需要调用 task.result() 才能拿到实际返回值。很多人误以为直接 await 就得到结果,导致 TypeError: object is not async 或卡在 as_completed 上不动。
常见错误写法:
async for result in asyncio.as_completed(tasks): # ❌ 错!as_completed 不支持 async for
- 正确方式是用
asyncio.create_task()包装任务后,再传给asyncio.as_completed() - 必须配合
for await+asyncio.wait()或更常见的:用普通for循环 +await task(注意不是awaitas_completed) - 实际流式处理时,推荐用
for task in asyncio.as_completed(tasks)+await task—— 这里task是协程对象,可直接await
如何真正“流式”获取最先完成的结果?
关键不是“谁先启动”,而是“谁先返回”。asyncio.as_completed() 的价值在于不等全部结束,只要任一任务完成就立刻处理,适合 IO 差异大的场景(比如混合了本地计算、HTTP 请求、数据库查询)。
- 所有任务必须已调度(即已用
asyncio.create_task()或asyncio.ensure_future()启动) - 不能把协程对象(如
fetch_url(url))直接塞进列表,否则会报TypeError: an integer is required (got type coroutine) - 示例中常见漏掉
await:错写成tasks.append(fetch_url(url)),应为tasks.append(asyncio.create_task(fetch_url(url)))
典型用法:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
tasks = [asyncio.create_task(fetch_url(u)) for u in urls]<br>for coro in asyncio.as_completed(tasks):<br> result = await coro # ✅ 此处 await 的是已完成的协程<br> print("Got:", result)
as_completed 和 gather 的性能差异在哪?
asyncio.gather() 是“全等式”:所有任务都跑完才统一返回结果列表;as_completed() 是“抢答式”:谁快谁先交卷。这对响应敏感型服务很关键——比如并发查 10 个 API,其中 2 个超快、3 个超慢,你希望前 2 个结果一出来就推给前端,而不是卡住等最后 1 个。
-
gather在失败时默认取消其余任务(除非设return_exceptions=True),而as_completed不影响其他任务运行 -
as_completed无法保证原始顺序,如果后续逻辑依赖索引位置,得自己存url → task映射关系 - 内存占用略高:每个
Task对象比裸协程多一层封装,但通常可忽略
遇到 TimeoutError 或 CancelledError 怎么办?
流式处理中最容易被忽略的是异常传播——as_completed 不过滤异常,一旦某个任务抛错,await task 就直接 raise,整个循环中断。这和预期的“跳过失败项继续收其他结果”不符。
- 必须对每个
await task做try/except,捕获asyncio.TimeoutError、CancelledError、Exception - 别用
return_exceptions=True(那是gather的参数,as_completed没这个选项) - 如果想统一设置超时,应在单个任务内部用
asyncio.wait_for(coro, timeout),而不是给as_completed加 timeout
安全写法:
for task in asyncio.as_completed(tasks):<br> try:<br> result = await task<br> process(result)<br> except (asyncio.TimeoutError, CancelledError):<br> continue # 或记录日志<br> except Exception as e:<br> log_error(e)
任务启动时机、异常处理、结果顺序——这三个点不提前想清楚,as_completed 很容易变成“看似流式,实则阻塞或崩溃”的陷阱。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










