1. 异步编程是什么? #

同步 异步
等待 I/O 线程卡住 让出执行权,先干别的
典型场景 简单脚本、传统 Django 视图 高并发 API、批量调外部接口
写法 def + requests async def + await

2. 定义协程函数 #

# 导入 asyncio 异步框架
import asyncio
# 定义一个异步函数 fetch_data,接受 name(字符串) 和 delay(浮点数) 两个参数
async def fetch_data(name: str, delay: float):
    # 打印任务开始的信息
    print(f"{name} 开始")
    # 异步休眠 delay 秒
    await asyncio.sleep(delay)
    # 打印任务完成的信息
    print(f"{name} 完成")
    # 返回参数 name
    return name

3. 等待异步操作完成 #

await asyncio.sleep(2)  # 等待 2 秒(不会阻塞主线程)

4. 启动事件循环并运行 #

# 导入 asyncio 异步框架
import asyncio

# 定义一个异步函数 fetch_data,参数 name 是字符串类型,delay 是浮点数类型
async def fetch_data(name: str, delay: float):
    # 打印任务开始的信息,输出 name 加上“开始”
    print(f"{name} 开始")
    # 异步休眠 delay 秒,不会阻塞主线程
    await asyncio.sleep(delay)
    # 打印任务完成的信息,输出 name 加上“完成”
    print(f"{name} 完成")
    # 返回参数 name
    return name

# 定义主异步函数 main
async def main():
    # 调用异步函数 fetch_data,参数为 "A" 和 2,并等待其完成
    await fetch_data("A", 2)

# 执行主异步函数 main
asyncio.run(main())

5. 多个协程并发 #

# 导入 asyncio 异步框架
import asyncio

# 定义一个异步函数 fetch_data,参数 name 是字符串类型,delay 是浮点数类型
async def fetch_data(name: str, delay: float):
    # 打印任务开始的信息,输出 name 加上“开始”
    print(f"{name} 开始")
    # 异步休眠 delay 秒,不会阻塞主线程
    await asyncio.sleep(delay)
    # 打印任务完成的信息,输出 name 加上“完成”
    print(f"{name} 完成")
    # 返回参数 name
    return name

# 定义主异步函数 main
async def main():
    # 使用 asyncio.gather 并发运行两个 fetch_data 协程,等待它们都完成,并将返回结果赋值给 results
    results = await asyncio.gather(
        # 启动 fetch_data 协程,参数为 "A" 和 2
        fetch_data("A", 2),
        # 启动 fetch_data 协程,参数为 "B" 和 1
        fetch_data("B", 1),
    )
    # 打印 results 列表,即 ["A", "B"]
    print(results)  # ['A', 'B']

# 执行主异步函数 main
asyncio.run(main())

6. 提前调度、稍后再 await #

# 导入 asyncio 模块
import asyncio

# 定义一个异步函数 fetch_data,参数 name 是字符串类型,delay 是浮点数类型
async def fetch_data(name: str, delay: float):
    # 打印任务开始的信息,输出 name 加上“开始”
    print(f"{name} 开始")
    # 异步休眠 delay 秒,不会阻塞主线程
    await asyncio.sleep(delay)
    # 打印任务完成的信息,输出 name 加上“完成”
    print(f"{name} 完成")
    # 返回参数 name
    return name

# 定义主异步函数 main
async def main():
    # 创建一个协程任务,调用 fetch_data,参数为"A"和2
    task = asyncio.create_task(fetch_data("A", 2))
    # 等待任务完成,并将返回值赋给 result
    result = await task
    # 打印返回的结果
    print(result)

# 执行主异步函数 main
asyncio.run(main())

7. 并发请求外部 API #

# 导入 asyncio 模块,用于异步 IO
import asyncio
# 导入 httpx 模块,用于发起 HTTP 请求
import httpx

# 定义一个异步函数 fetch_status,用于获取单个 URL 的状态码
async def fetch_status(client: httpx.AsyncClient, url: str) -> int:
    # 使用传入的 AsyncClient 实例异步请求给定 URL,设置超时时间为 10 秒
    resp = await client.get(url, timeout=10.0)
    # 返回响应的状态码
    return resp.status_code

# 定义主异步函数 main
async def main():
    # 定义一个包含待请求 URL 的列表
    urls = [
        "https://httpbin.org/delay/1",
        "https://httpbin.org/delay/2",
    ]
    # 使用异步上下文管理器创建 AsyncClient 实例,确保复用连接
    async with httpx.AsyncClient() as client:
        # 并发执行对每个 URL 的 fetch_status 调用,收集所有返回值
        codes = await asyncio.gather(*[fetch_status(client, u) for u in urls])
    # 打印所有状态码组成的列表
    print(codes)

# 执行主异步函数 main,开启事件循环
asyncio.run(main())

安装:pip install httpx 或 uv add httpx。

8. 异常与超时 #

# 导入asyncio模块,用于异步编程
import asyncio
# 导入httpx模块,用于发送HTTP请求
import httpx

# 定义一个带超时和异常处理功能的异步请求函数
async def safe_fetch(client, url):
    # 尝试执行以下代码块
    try:
        # 使用client发送GET请求,请求超时设为5秒
        r = await client.get(url, timeout=5.0)
        # 检查请求结果是否有异常,若有则抛出异常
        r.raise_for_status()
        # 返回响应内容
        return r.text
    # 捕获httpx.HTTPError异常
    except httpx.HTTPError as e:
        # 返回错误信息字符串
        return f"失败: {e}"

# 定义主异步函数
async def main():
    # 定义一个URL列表
    urls = [
        # 第一个网址
        "https://www.example.com",
        # 第二个网址,可能访问失败
        "https://www.nonexistentwebsite123456789.com",  # 一个可能失败的地址
        # 第三个网址
        "https://httpbin.org/get"
    ]
    # 创建异步HTTP客户端
    async with httpx.AsyncClient() as client:
        # 为每个URL生成一个safe_fetch任务列表
        tasks = [safe_fetch(client, url) for url in urls]
        # 异步并发执行所有任务,失败的任务返回异常对象而不影响其他
        results = await asyncio.gather(*tasks, return_exceptions=True)
        # 遍历所有请求结果
        for i, result in enumerate(results):
            # 打印每个URL返回的结果
            print(f"URL {i}: {result}")

# 运行主异步函数main()
asyncio.run(main())

9. 项目开发要点 #

10. 总结 #

概念 要点
async / await 定义与等待协程
asyncio.run 程序入口
gather 并发多个 I/O
异步 HTTP httpx + async with Client
禁忌 在 async 里 sleep / 同步 requests