asyncio.wait在return_when=FIRST_COMPLETED/FIRST_EXCEPTION时会返回部分完成任务,done集合包含已结束(含异常)的任务,pending为未完成任务,需手动检查task.exception()区分成功与失败。
当传入的协程中,有些已结束(成功或异常),有些还在运行,asyncio.wait 就会立即返回已完成和未完成两组 Task 对象。它本身不区分“成功”和“失败”,只看是否已结束——也就是说,哪怕某个任务抛了异常,它也会出现在 done 集合里。
所以“部分任务成功”的判断逻辑必须你自己写:遍历 done,用 task.exception() 检查是否出错,再用 task.result() 获取值(仅当无异常时安全调用)。
done 里的 task 调 result(),可能触发未处理异常asyncio.wait 默认是 return_when=asyncio.FIRST_COMPLETED,想等一批完成得显式设成 asyncio.FIRST_EXCEPTION 或 asyncio.ALL_COMPLETED,但注意后者会卡住直到全部结束wait(..., timeout=1) 能“超时后只取成功的”,其实超时后未完成任务仍在 pending 中,done 里只有那些真结束了的(含失败的)核心是分离“已完成”和“成功”两个条件。先过滤出 done 中没异常的 task,再统一取结果:
done, pending = await asyncio.wait(tasks, timeout=2)successful_results = []for task in done: if task.exception() is None: successful_results.append(task.result())
这段代码不会因某个 task 抛异常而中断,也不会尝试从失败 task 中取 result。如果需要保留失败原因用于日志或重试,可以额外收集 task.exception()。
立即学习“Python免费学习笔记(深入)”;
task.cancelled() 替代异常检查——取消和异常是两回事,cancelled() 为 True 时 exception() 可能返回 CancelledError
None 是合法结果,不能靠 if task.result() 判定成功,必须依赖 exception() 是否为 None
done 是 set,顺序不保证;如需按原始顺序整理结果,得提前给 task 加索引或用 asyncio.create_task 包装时绑定上下文timeout=0 不是“不等待”,而是“最多等 0 秒”——它会立刻返回当前已结束的 task(哪怕一个都没有,done 就是空集)。而 return_when=asyncio.FIRST_COMPLETED 会至少等到第一个 task 结束才返回。
两者组合使用很常见:比如你想“立刻检查有没有现成结果,没有就等第一个完成”,就得先 wait(..., timeout=0),若 done 为空,再 wait(..., return_when=FIRST_COMPLETED)。
timeout=0 在事件循环刚启动时可能返回空 done,不是 bug,是预期行为asyncio.to_thread),done 会长期为空,需配合重试或 fallback 逻辑return_when 的其他选项如 ALL_COMPLETED 或 FIRST_EXCEPTION 会改变阻塞行为,选错会导致程序卡住或过早退出最常见原因是 task 已被取消或已异常结束,但你跳过了 exception() 检查。例如:
task = asyncio.create_task(some_coro())await asyncio.sleep(0.1)task.cancel()done, _ = await asyncio.wait([task])# 此时 task in done 为 True,但 task.result() 会 raise CancelledError
另一个容易忽略的点:task 可能在 wait 返回前就完成了,但你没及时处理,之后又手动 cancel() 了它——这时 exception() 返回的是 CancelledError,不是原始异常。
task.done() 再调 task.exception() 或 task.result(),避免 InvalidStateError
asyncio.ensure_future 创建的,行为一致;但用 loop.create_task 时要注意 loop 是否已关闭finally 块里清理 pending task 时,别忘了用 cancel() + await asyncio.wait(...) 等待它们真正结束,否则可能残留未处理异常实际用的时候,最关键的不是等多少个、超时多久,而是每次从 done 拿 task 后,必须做三件事:确认 done()、检查 exception()、最后才取 result()。漏掉中间任一环,都可能让看似“部分成功”的逻辑突然崩在异常上。