asyncio.wait_for超时抛timeouterror而非返回默认值,因其设计目标是强制中断而非容错;必须用try/except捕获并手动提供fallback,同时确保被取消协程的资源清理。

asyncio.wait_for 超时后抛出 asyncio.TimeoutError,它不会自动返回默认值——你必须显式捕获并提供 fallback。
为什么 asyncio.wait_for 不返回默认值
它的设计目标是“强制中断等待”,不是“兜底容错”。超时即异常,和 dict.get(key, default) 这类带默认行为的接口逻辑完全不同。一旦超时,协程被取消,后续逻辑不会继续执行,除非你主动处理这个异常。
-
asyncio.wait_for返回的是原协程的结果,不是包装后的“可选结果” - 被取消的协程如果没做清理(如关闭连接、释放锁),可能引发资源泄漏
- 若原协程本身也抛异常(比如网络错误),
TimeoutError会覆盖它——除非用return_when=asyncio.FIRST_EXCEPTION配合asyncio.wait
正确写法:用 try/except 捕获 asyncio.TimeoutError
最直接、最可控的方式就是手动 catch 并 return 默认值。不要试图用装饰器或封装函数“隐藏”这一层逻辑,容易掩盖取消状态或导致二次 await 已取消的协程。
import asyncio
async def fetch_data():
await asyncio.sleep(3)
return {"status": "ok"}
async def fetch_with_timeout():
try:
return await asyncio.wait_for(fetch_data(), timeout=1.0)
except asyncio.TimeoutError:
return {"status": "timeout", "data": None}
- 超时后返回字典,类型和成功路径一致,调用方无需类型检查
- 不要在
except块里再 await 其他协程(除非明确需要降级逻辑),否则可能再次超时 - 若需记录日志,建议在
except中加logging.warning,而不是 print
注意协程取消后的状态残留问题
被 wait_for 取消的协程会抛出 asyncio.CancelledError,但如果你没在 fetch_data 内部处理它,它可能静默吞掉或导致未关闭的 socket、未 commit 的事务等。
- 在可能长耗时的协程中,应使用
try/finally或async with确保资源释放 - 避免在
except asyncio.TimeoutError后直接忽略原任务——可以用asyncio.create_task启动一个后台 cleanup,但别 await 它 - 测试时可用
asyncio.current_task().cancelled()检查是否已被取消,辅助调试
真正麻烦的不是怎么写默认值,而是默认值背后那个被中断却没清理干净的协程——它可能正在往数据库写一半数据,或者持有某个关键锁。超时处理从来不只是“换个返回值”的事。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











