async函数调用后立刻返回一个coroutine对象,该对象未执行,仅表示待调度的异步任务;必须通过await或asyncio.run()驱动运行。

async 函数调用后立刻返回什么
调用 async def 定义的函数,不会真正执行函数体,而是立即返回一个 coroutine 对象。这个对象本身不运行,只是个待调度的“任务描述”。
常见错误是直接打印或忽略返回值,误以为函数已执行:
async def fetch_data():
return "done"
result = fetch_data() # ← 这里 result 是
print(result) # ,不是 "done"
- 必须用
await(在另一个async函数内)或asyncio.run()驱动它运行 -
await只能在async def函数内部使用,不能在普通函数或交互式顶层直接写 - 试图对 coroutine 对象做
result + "!"会报TypeError: unsupported operand type(s)
await 表达式到底做了什么
await 不是“等待完成”,而是“让出控制权,挂起当前协程,把 CPU 让给事件循环去跑别的协程”。它只在遇到真正的异步挂起点(如 asyncio.sleep()、aiohttp.get()、await asyncio.Lock().acquire())时才暂停;纯计算不会挂起。
示例中看似“同步”的代码,实际可能不阻塞:
立即学习“Python免费学习笔记(深入)”;
async def work():
print("start")
await asyncio.sleep(0) # ← 真正的挂起点
print("after sleep")
x = sum(i for i in range(10**6)) # ← 纯计算,不挂起,会阻塞整个事件循环-
await后面必须是可等待对象(Awaitable),比如coroutine、Task、Future,不能是普通函数返回值或数字 - 写
await 42会报TypeError: object int can't be used in 'await' expression - 多次
await同一个协程对象会报RuntimeError: cannot reuse already awaited coroutine
asyncio.run() 的隐含行为
asyncio.run(main()) 不只是启动协程,它会:新建一个事件循环、把 main() 包装成 Task、运行直到完成、然后关闭循环并清理所有未完成的 Task(触发 CancelledError)。
这本书给出了一份关于python这门优美语言的精要的参考。作者通过一个完整而清晰的入门指引将你带入python的乐园,随后在语法、类型和对象、运算符与表达式、控制流函数与函数编程、类及面向对象编程、模块和包、输入输出、执行环境等多方面给出了详尽的讲解。如果你想加入 python的世界,David M beazley的这本书可不要错过哦。 (封面是最新英文版的,中文版貌似只译到第二版)
这意味着:
- 不能在已有事件循环的环境中(如 Jupyter、Django/Flask 异步视图、或已调用
asyncio.get_event_loop()的脚本)重复调用asyncio.run(),会报RuntimeError: asyncio.run() cannot be called from a running event loop - 若
main()内部启用了后台Task(如asyncio.create_task(...)),且没显式await或asyncio.wait_for(),它们可能被强制取消 -
asyncio.run()总是创建新循环,所以无法跨调用共享asyncio.Queue或asyncio.Event等对象
Task 和 create_task() 的生命周期管理
用 asyncio.create_task() 提交的协程会立即被调度,但它的完成时间不可控——你得自己决定是否等待它、如何捕获异常、是否允许它在主协程退出后继续跑。
典型陷阱:
async def background_job():
await asyncio.sleep(2)
print("done")
async def main():
asyncio.create_task(background_job()) # ← 没有变量接收,也没 await
await asyncio.sleep(0.1) # 主协程很快结束
这段代码大概率看不到 "done" 输出,因为 background_job() 被创建后还没来得及执行完,事件循环就退出了。
- 推荐保存 task 到变量:
task = asyncio.create_task(...),再用await task或asyncio.gather(task, ...) - 若需“发射即忘”,至少加
asyncio.shield(task)防止被取消,或用asyncio.create_task(..., name="xxx")方便调试 - 未被 await 的 task 报错时(如网络异常),错误会被静默吞掉,除非你调用
task.exception()主动检查
真正难的不是语法,是判断某段逻辑该不该拆成 async、哪个 await 实际构成瓶颈、以及什么时候该用 Task 而不是链式 await。这些没法靠记住规则解决,得看 asyncio.Task.all_tasks() 和 asyncio.current_task() 在运行时吐出什么。









