【python知识点-async、await】

3 阅读4分钟

asyncawait 是现代编程语言(如 Python、JavaScript、C# 等)中用于处理异步编程的核心关键字。

简单来说,它们让编写“非阻塞”代码变得像编写“同步”代码一样简单直观。

1. 核心概念通俗解释

想象你在一家餐厅吃饭:

  • 同步编程(传统方式) : 你点了一道菜(发起请求),然后死死盯着厨房,什么都不做,直到厨师把菜端出来(等待结果),你才开始吃下一口或者点饮料。如果厨师做饭很慢,你就一直干等着,效率极低。

  • 异步编程(async/await) : 你点了一道菜(发起异步请求),服务员给你一个号码牌(await 一个任务)。

    • 你不需要盯着厨房,你可以利用这段时间去玩手机、喝口水或者跟朋友聊天(程序去执行其他任务)。
    • 当菜做好了(任务完成),服务员叫你的号,你立刻回来拿起筷子吃(代码继续执行后续逻辑)。

asyncawait 的作用就是管理这个“拿号码牌”和“回来继续吃”的过程。


2. 关键字详解

async (定义异步函数)

  • 位置:放在函数定义前面,如 async def (Python) 或 async function (JS)。
  • 作用:声明这个函数是异步的
  • 结果:调用这个函数时,它不会立即执行函数体内的代码,而是返回一个 Promise (JS) 或 Coroutine 对象 (Python)。这就像是你拿到了一个“未来会完成的承诺”。

await (等待异步结果)

  • 位置:只能用在 async 函数内部,放在一个异步操作前面,如 await fetch_data()

  • 作用

    1. 暂停当前函数的执行,把控制权交还给事件循环(Event Loop),让程序可以去干别的事。
    2. 等待后面的异步操作完成。
    3. 一旦操作完成,恢复函数执行,并拿到结果。
  • 注意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. 为什么要用它?

  1. 提高性能:在处理 I/O 密集型任务(如读写文件、网络请求、数据库查询)时,程序不需要在等待响应时空转,可以处理成千上万个并发连接(这也是 Node.js 和现代 Python 爬虫高效的原因)。
  2. 代码可读性:在没有 async/await 之前,异步代码通常充满了“回调地狱”(Callback Hell),层层嵌套,难以阅读。async/await 让异步代码看起来像同步代码一样线性、清晰。
  3. 避免界面卡顿:在前端开发中,如果使用同步方式请求网络,浏览器界面会卡死直到请求结束。使用 async/await 可以保证界面流畅响应用户操作。

5. 常见误区

  • 误区await 会让程序变慢。

    • 真相:单个看确实是在“等”,但它允许多个任务同时等待,从而大幅减少总等待时间。
  • 误区async 函数会自动多线程运行。

    • 真相:不是!async/await 通常是单线程的(基于事件循环)。它通过协作式多任务(Cooperative Multitasking)来实现并发。如果一个任务里进行了大量的 CPU 计算(如复杂的数学运算)且没有 await,它依然会阻塞整个线程。CPU 密集型任务通常需要配合多进程或多线程。

总结

  • async:告诉编译器/解释器,“这个函数包含异步操作,调用它会返回一个承诺”。
  • await:告诉程序,“在这个操作完成前,先暂停这个函数,去忙别的,等好了再回来继续”。

它们是解决高并发 I/O 问题的神器。