await 是协程暂停并交还控制权给事件循环的协作式调度机制,依赖 awaitable、事件循环和状态机协程三者配合;它非阻塞,本质是带语义的 yield,仅适用于 async def 函数。

当你写下 await,Python 并不是“暂停当前函数然后等结果回来”,而是把控制权交还给事件循环,让其他协程有机会运行——这背后是一套协作式调度机制,依赖于 可等待对象(awaitable)、事件循环(event loop) 和 状态机式的协程对象 三者配合。
await 不是阻塞,而是 yield 的升级版
一个 async def 函数被调用时,返回的是一个 协程对象(coroutine object),它本质上是一个可以被暂停和恢复的状态机。每次遇到 await,协程就暂停执行,并把自身(作为待恢复的“任务”)交还给事件循环。这和生成器中的 yield 行为高度相似,但语义更明确:你是在等待另一个 awaitable 完成。
- 普通函数调用是同步栈式执行,会一直跑到
return或异常; -
await触发的是协程的__await__()方法,它必须返回一个迭代器(通常是通过yield实现),事件循环靠这个迭代器一步步驱动协程前进; - 真正“挂起”的不是线程,而是当前协程的状态(局部变量、指令指针等),由 Python 解释器在 C 层面保存和恢复。
awaitable 对象必须满足什么条件?
只有符合 awaitable 协议的对象才能跟在 await 后面。Python 会按顺序检查:
- 对象实现了
__await__方法,且返回一个迭代器(如asyncio.Future、自定义类); - 对象本身是协程对象(
async def返回值); - 对象是
asyncio.Task或asyncio.Future的实例(它们内部也实现了__await__)。
如果写 await 42 或 await "hello",Python 会立刻抛出 TypeError: object int can't be used in 'await' expression,因为这些类型没实现协议。
立即学习“Python免费学习笔记(深入)”;
事件循环如何调度 await 的“等待”?
事件循环(如 asyncio.run() 启动的那个)并不真的“等”,而是维护一个就绪队列和一个等待队列:
- 当协程
await一个尚未完成的Future,它会被注册到该Future的回调列表中; -
Future完成(比如 I/O 就绪、set_result()被调用)时,会通知事件循环:“我好了”; - 事件循环把对应的协程重新加入就绪队列,在下一轮循环中继续驱动它执行到下一个
await或结束。
整个过程不涉及线程切换或系统等待调用(除非底层用了 select/epoll 等 I/O 多路复用),纯用户态协作调度。
为什么不能在普通函数里用 await?
因为 await 是语法糖,只对 async def 定义的函数有效。它的存在本身就要求函数体被编译成一种特殊字节码(YIELD_FROM 指令),而普通函数没有这个能力。试图在非协程中写 await 会在语法解析阶段报错:SyntaxError: 'await' outside async function。
如果你需要从同步上下文触发异步逻辑,必须显式进入事件循环,例如用 asyncio.run(coro()) 或 loop.run_until_complete(coro()) —— 这些函数才是真正启动调度器的地方。










