await后只能跟协程对象、Task、Future或实现__await__的对象;常见错误包括await同步函数、列表或未加async的函数调用,正确做法是确保函数用async def定义、并发用asyncio.gather、链式步骤显式赋值并单独处理异常。

await 后面只能跟协程对象,不是什么都能等
很多人写 await 时下意识往后面塞函数调用、列表、甚至 None,结果直接报 TypeError: object X can't be used in 'await' expression。根本原因是:只有协程对象(coroutine)、任务(Task)、Future 或实现了 __await__ 的对象才能被 await。
常见错误包括:await time.sleep(1)(time.sleep 是同步阻塞函数)、await [coro1(), coro2()](列表不是可等待对象)、await my_func()(但 my_func 没加 async def)。
- 确认函数定义用了
async def,调用后返回的是协程对象,不是普通函数返回值 - 想并发执行多个协程?用
await asyncio.gather(coro1(), coro2()),别直接await [coro1(), coro2()] - 需要在链中插入同步操作(比如日志、简单计算)?直接写,不用
await;真要“暂停”得用await asyncio.sleep(0)让出控制权
链式 await 不等于自动串行化,顺序依赖得自己保证
写成 await a(); await b(); await c() 看似链式,但实际只是语法糖——它不提供错误传播、超时控制或中间状态管理。一旦 b() 抛异常,c() 就不会执行,但你也拿不到 a() 的返回值做后续判断,除非手动存变量。
典型场景是 API 调用链:获取 token → 拿 token 请求数据 → 处理响应。如果第二步失败,你可能还想重试第一步,或者清理 token 缓存。
- 每一步的返回值建议显式赋给变量,比如
token = await get_token(),避免在下一行直接await fetch_data(token)导致调试时看不到中间值 - 需要条件跳过某步?用
if判断,不要靠await自动跳过(它不会跳) - 别把多个逻辑塞进一个协程里假装“链式”,拆开更利于测试和复用,比如
validate_input()、call_api()、format_result()各自独立
asyncio.create_task() 和直接 await 的性能与取消行为差异
链式执行时,有时你会纠结:该用 await coro() 还是 task = asyncio.create_task(coro()); await task?区别不在“能不能链”,而在“谁控制生命周期”。直接 await 是严格串行且不可取消的;而 create_task 启动后就脱离当前作用域,可以被外部取消、加超时、或与其他任务协同调度。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
比如你写了个链:下载 → 解压 → 校验。若解压耗时太久,用户点了取消,直接 await unzip() 无法中断;但用 task = create_task(unzip()),之后调 task.cancel() 就能触发协程内的 CancelledError。
- 纯内部逻辑、无外部干预需求,用
await coro()更轻量、更直观 - 需要支持取消、超时、或与其他任务并行协作(比如一边链式处理,一边轮询进度),必须用
asyncio.create_task() -
create_task创建的任务默认不被引用,如果没保存变量或加入asyncio.all_tasks()管理,可能被 GC 掉导致静默失败
错误处理不是加个 try-except 就完事
链式 await 中某个环节抛异常,会立刻终止后续步骤,但 Python 的异常传播机制会让最外层 try 捕获到它——问题在于,你不知道是哪一环崩的,也不知道前面成功步骤的返回值是否还有效。
比如 await login(); await load_profile(); await render_page(),若 load_profile() 失败,login() 返回的 session 对象可能已过期,但你没法自动清理。
- 每个
await建议单独包try/except,而不是整个链套一个大try,否则定位困难 - 涉及资源获取(如打开连接、申请 token),务必配对清理逻辑,用
async with或finally块,别指望链断了系统自动回收 - 不要在
except里直接return或raise就结束,考虑是否要记录上下文,比如logger.error("failed at %s with %s", "load_profile", e)
事情说清了就结束。链式 await 表面简单,真正难的是状态衔接、错误边界和取消语义——这些地方没显式处理,运行时才暴露。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










