在 Python 中使用 async def 定义的异步函数,和普通函数有着本质区别。直接写一句 func() 并不会执行函数体里的代码,而是得到一个协程对象。如果不当回事随手调用,程序既不会报错中断,也不会产生你期望的结果,这种随意调用正是很多异步 bug 的来源。

为什么异步函数不能像普通函数一样调用
普通函数调用时,解释器进入函数体按顺序执行,遇到 return 就把结果交回来。异步函数则不同,它的函数体被包装成一个协程,只有在被事件循环调度并且真正被 await 时才会运行。下面这段代码就是典型的错误用法:
import asyncio
async def fetch_data():
await asyncio.sleep(1)
return "data"
# 错误:直接调用,只得到协程对象,函数体未执行
result = fetch_data()
print(result) # 输出类似 <coroutine object fetch_data at 0x...>
上面的 fetch_data() 没有被执行,result 只是一个未运行的协程。如果程序结束前协程没有被 await,Python 还会给出“协程从未被等待”的警告。
常见的随意调用错误
- 在同步函数里直接调用异步函数,却不通过事件循环驱动。
- 把异步函数当回调传给普通接口,导致协程对象被丢弃。
- 在循环里调用异步函数却忘了收集任务,造成大量未等待协程。
正确的调用方式
使用 asyncio.run 启动顶层协程
如果是程序入口处的异步逻辑,可以用 asyncio.run 来运行:
import asyncio
async def main():
data = await fetch_data()
print(data)
asyncio.run(main())
在异步函数内部用 await 调用
已经处于异步上下文时,直接用 await 等待另一个异步函数即可:
async def process():
res = await fetch_data()
return res.upper()
并发调用多个异步函数
需要同时跑多个协程时,使用 asyncio.gather:
async def main():
results = await asyncio.gather(fetch_data(), fetch_data())
print(results)
总结
Python 异步函数返回协程对象,必须由事件循环调度并通过 await 触发执行。随意调用不仅拿不到结果,还会留下未等待协程隐患。理解协程与事件循环的关系,使用 asyncio.run、await 与 gather 等正确方式,才能发挥异步编程的价值。