async 和 await 是现代编程语言(如 Python、JavaScript、C# 等)中用于处理异步编程的核心关键字。
简单来说,它们让编写“非阻塞”代码变得像编写“同步”代码一样简单直观。
1. 核心概念通俗解释
想象你在一家餐厅吃饭:
-
同步编程(传统方式) : 你点了一道菜(发起请求),然后死死盯着厨房,什么都不做,直到厨师把菜端出来(等待结果),你才开始吃下一口或者点饮料。如果厨师做饭很慢,你就一直干等着,效率极低。
-
异步编程(async/await) : 你点了一道菜(发起异步请求),服务员给你一个号码牌(
await一个任务)。- 你不需要盯着厨房,你可以利用这段时间去玩手机、喝口水或者跟朋友聊天(程序去执行其他任务)。
- 当菜做好了(任务完成),服务员叫你的号,你立刻回来拿起筷子吃(代码继续执行后续逻辑)。
async 和 await 的作用就是管理这个“拿号码牌”和“回来继续吃”的过程。
2. 关键字详解
async (定义异步函数)
- 位置:放在函数定义前面,如
async def(Python) 或async function(JS)。 - 作用:声明这个函数是异步的。
- 结果:调用这个函数时,它不会立即执行函数体内的代码,而是返回一个 Promise (JS) 或 Coroutine 对象 (Python)。这就像是你拿到了一个“未来会完成的承诺”。
await (等待异步结果)
-
位置:只能用在
async函数内部,放在一个异步操作前面,如await fetch_data()。 -
作用:
- 暂停当前函数的执行,把控制权交还给事件循环(Event Loop),让程序可以去干别的事。
- 等待后面的异步操作完成。
- 一旦操作完成,恢复函数执行,并拿到结果。
-
注意:
await不会阻塞整个程序,它只是暂停了当前这个函数。
3. 代码示例 (以 Python 为例)
假设我们要模拟两个耗时操作:下载文件 A 和下载文件 B。
❌ 同步写法(慢)
必须等 A 下完,才能开始下 B。总耗时 = 时间A + 时间B。
import time
def download(name):
print(f"开始下载 {name}...")
time.sleep(2) # 模拟耗时 2 秒
print(f"{name} 下载完成")
return f"{name} 的数据"
def main():
data_a = download("文件A") # 阻塞 2 秒
data_b = download("文件B") # 再阻塞 2 秒
print("全部完成")
# 总耗时约 4 秒
✅ 异步写法(快)
启动下载 A 后,立刻去启动下载 B。两者同时进行。总耗时 ≈ max(时间A, 时间B)。
import asyncio
# 1. 定义异步函数 (async)
async def download(name):
print(f"开始下载 {name}...")
await asyncio.sleep(2) # 2. 模拟耗时,但不会阻塞主线程
print(f"{name} 下载完成")
return f"{name} 的数据"
# 3. 定义主异步入口
async def main():
# 创建两个任务,让它们并发运行
task_a = asyncio.create_task(download("文件A"))
task_b = asyncio.create_task(download("文件B"))
# 4. 等待结果 (await)
# 这里虽然写了 await,但因为任务已经在上一步启动了,
# 程序会挂起等待它们完成,而不是傻傻地一个个等。
data_a = await task_a
data_b = await task_b
print("全部完成")
# 运行异步主函数
asyncio.run(main())
# 总耗时约 2 秒 (并行执行)
4. 为什么要用它?
- 提高性能:在处理 I/O 密集型任务(如读写文件、网络请求、数据库查询)时,程序不需要在等待响应时空转,可以处理成千上万个并发连接(这也是 Node.js 和现代 Python 爬虫高效的原因)。
- 代码可读性:在没有
async/await之前,异步代码通常充满了“回调地狱”(Callback Hell),层层嵌套,难以阅读。async/await让异步代码看起来像同步代码一样线性、清晰。 - 避免界面卡顿:在前端开发中,如果使用同步方式请求网络,浏览器界面会卡死直到请求结束。使用
async/await可以保证界面流畅响应用户操作。
5. 常见误区
-
误区:
await会让程序变慢。- 真相:单个看确实是在“等”,但它允许多个任务同时等待,从而大幅减少总等待时间。
-
误区:
async函数会自动多线程运行。- 真相:不是!
async/await通常是单线程的(基于事件循环)。它通过协作式多任务(Cooperative Multitasking)来实现并发。如果一个任务里进行了大量的 CPU 计算(如复杂的数学运算)且没有await,它依然会阻塞整个线程。CPU 密集型任务通常需要配合多进程或多线程。
- 真相:不是!
总结
async:告诉编译器/解释器,“这个函数包含异步操作,调用它会返回一个承诺”。await:告诉程序,“在这个操作完成前,先暂停这个函数,去忙别的,等好了再回来继续”。
它们是解决高并发 I/O 问题的神器。