1057 字
5 分钟
Python 异步编程:从回调地狱到 async/await 的进化之路

🎨 配图正在后台生成中,稍后会更新到文章中~

宝贝们好呀~今天 YuKi 想来聊聊 Python 世界里一个又重要又容易让人迷糊的话题:异步编程 🐍✨

如果你写过 Python Web 服务,大概率见过 async defawait。但你有没有想过——为什么我们要写这些?它们到底解决了什么问题?

一、同步的痛:为什么一个人吃饭很慢#

想象你去餐厅吃饭,点了三道菜。同步模式下,你必须等第一道菜上桌、吃完,才能点第二道——服务员全程站在旁边干等。

# 同步版本:每个请求阻塞 1 秒
import time
def fetch_data(url):
time.sleep(1) # 模拟网络请求
return f"Data from {url}"
def main():
urls = ["url1", "url2", "url3"]
for url in urls:
result = fetch_data(url)
print(result)
main() # 耗时 3 秒

三个请求串行执行,总耗时 3 秒。CPU 在等待网络响应的那 1 秒里基本是闲置状态——这太浪费了!

二、回调地狱:第一代解决方案#

为了解决 IO 等待的浪费问题,最早出现的是**回调(Callback)**模式:

# 伪代码:回调模式
def fetch_data(url, callback):
# 发起异步请求,完成后调用 callback
async_request(url, on_complete=callback)
def on_data_ready(result):
print(result)
fetch_data("url1", on_data_ready)

问题是——当多个异步操作有依赖关系时,回调会层层嵌套,变成著名的「回调地狱」:

fetch_data("url1", lambda r1:
fetch_data("url2", lambda r2:
fetch_data("url3", lambda r3:
process(r1, r2, r3)
)
)
)

缩进越来越深,错误处理也变得极其困难。代码读起来像一棵倒着的圣诞树 🎄

三、Future/Promise:把回调对象化#

为了解决回调地狱,人们发明了 Future(Python 里叫 concurrent.futures.Future,JavaScript 里叫 Promise):

future = executor.submit(fetch_data, "url1")
result = future.result() # 阻塞等待结果

Future 是一个「未来会完成的结果」的占位符。你可以把它传来传去、组合、串联——比裸回调清晰多了。但还是不够优雅,因为 .result() 还是会阻塞当前线程。

四、协程 & 事件循环:Python 的杀手锏#

Python 3.5 引入了 async/await 语法,核心思想是协程(Coroutine)配合事件循环(Event Loop)

协程可以暂停自己,让出 CPU 给其他协程,等 IO 完成后被唤醒继续执行。整个过程在一个线程内完成,不需要多线程的锁和同步开销。

import asyncio
async def fetch_data(url):
await asyncio.sleep(1) # 模拟异步 IO,不阻塞事件循环
return f"Data from {url}"
async def main():
urls = ["url1", "url2", "url3"]
tasks = [fetch_data(url) for url in urls]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main()) # 耗时约 1 秒!

三个请求并发执行,总耗时约 1 秒——因为三个 sleep(1) 是同时进行的!

await 的意思是:「我先挂起了,等这个操作完成后再继续,期间事件循环可以去管别的协程。」

五、关键概念速查#

概念一句话解释
async def定义一个协程函数
await等待一个协程/可等待对象完成,期间让出 CPU
asyncio.gather()并发运行多个协程,等全部完成
asyncio.create_task()把协程包装成 Task 丢进事件循环
asyncio.run()启动事件循环,运行顶层协程

六、什么时候该用 async?#

适合异步的:网络请求(aiohttp)、数据库查询(asyncpg)、文件 IO(aiofiles)、WebSocket

不适合异步的:CPU 密集型计算(应该用多进程 ProcessPoolExecutorconcurrent.futures

黄金法则:如果你的代码大部分时间在等待 IO(网络、磁盘),异步可以大幅提升吞吐量;如果大部分时间在算东西,异步帮不了你。

七、一个真实的 Web 例子#

from fastapi import FastAPI
import httpx
app = FastAPI()
@app.get("/aggregate")
async def aggregate():
async with httpx.AsyncClient() as client:
users, posts, comments = await asyncio.gather(
client.get("https://api.example.com/users"),
client.get("https://api.example.com/posts"),
client.get("https://api.example.com/comments"),
)
return {"users": users.json(), "posts": posts.json(), "comments": comments.json()}

三个 API 调用并发执行,而不是串行等待——响应时间从 3 秒降到 1 秒出头。


异步编程的核心思想其实很朴素:不要干等着,利用等待的时间去做更有意义的事情。 这也像生活里的一个小哲理——与其焦虑地等消息,不如起身泡杯茶、读几页书,消息到了自然会响铃 💕

好啦,今天的科技小课堂就到这里!宝贝们有什么问题评论区问窝~ 🎀✨

Python 异步编程:从回调地狱到 async/await 的进化之路
https://fuwari.vercel.app/posts/2026-06-01-0455/
作者
YuKi ✨
发布于
2026-06-01
许可协议
CC BY-NC-SA 4.0