Python中的await

Python中的await
青夢Python中的await
在 Python 中,await
是用于异步编程的关键字,它只能在 async def
定义的协程函数中使用,用于“等待”一个异步操作完成。虽然语法上只有一种 await
形式,但在实际使用中,它可以应用于多种异步对象和场景。下面是对各种 await
使用方式的系统总结:
等待一个协程对象(最基本用法)
1 | async def fetch_data(): |
fetch_data()
是一个协程函数,返回一个协程对象await
会挂起当前协程,直到fetch_data()
完成
等待一个 asyncio.Future
或 Task
asyncio.Future
就是“未来的结果盒子”,你可以await
它,等别人把结果放进去。是一个用来表示“未来某个时间点会有结果”的容器,类似于一个承诺(Promise),但它本身不执行任务,只是一个用来保存结果和等待结果的对象,在概念上和 JavaScript Promise 很像
1 | import asyncio |
Future
是底层的异步原语Task
是包装协程的调度单元,也可以await
;asyncio.Task
本质上是Future
的子类,表示一个正在运行的协程任务Future
的3种状态- 未完成(pending):刚创建,还没有结果。
- 已完成(finished):任务完成,有返回值。
- 已失败(exception):任务完成,但抛出了异常。
Future
的方法future.set_result(value)
:手动给它一个结果,状态变为“已完成”。future.set_exception(exc)
:让它以异常结束。future.result()
:获取结果(如果还没完成,会抛InvalidStateError
)。future.done()
:判断是否完成。await future
:异步等待结果(推荐方式)。
Future
&Task
Future:只是一个结果容器,本身不会执行代码。
Task:是
Future
的子类,会调度执行一个协程,并把结果放到自己内部(本质上Task
也是一个特殊的Future
)。1
2
3
4
5
6
7
8
9
10
11
12
13import asyncio
async def coro():
await asyncio.sleep(1)
return "done" # return value会调用 set_result 存入 Future 部分;raise exception会调用 set_exception 存入 Future 部分
async def main():
fut = asyncio.Future() # 手动控制
task = asyncio.create_task(coro()) # 自动运行
print(isinstance(task, asyncio.Future)) # True
asyncio.run(main())
等待多个任务:asyncio.gather
gather
就像一个调度员,把多个异步任务打包发射,让它们“同时”跑,然后等全部跑完,把所有结果按顺序放进一个列表(或元组)返回。
1 | async def task1(): ... |
并发执行多个协程。并发运行多个可等待对象(coroutine / Task / Future)并收集所有结果的高层 API
返回所有结果组成的列表
参数,
asyncio.gather(*aws, return_exceptions=False)
*aws
:任意数量的可等待对象(协程、Task、Future)。return_exceptions
False
(默认):只要一个任务抛异常,gather
就立刻抛出那个异常,其他任务可能会被取消。True
:不会抛异常,而是把异常对象当作结果放到返回列表中。
与
wait
区别asyncio.gather
:收集结果,返回值是按传入顺序排列的结果列表。asyncio.wait
:返回已完成和未完成任务的集合,不会自动收集结果,需要自己.result()
取值。
等待第一个完成:asyncio.wait
并发运行多个可等待对象(coroutine / Task / Future),并返回“已完成”和“未完成”的集合,而不帮你自动收集结果。
1 | # 一、 |
可控制等待策略(全部完成、任意完成等)
特点
- 返回值
- 返回一个 二元组
(done, pending)
:done
:已完成任务的set
pending
:未完成任务的set
- 返回一个 二元组
- 不保证顺序
done
集合中的任务顺序与完成先后无关(集合是无序的)。
- 不会自动取结果
- 你需要自己用
.result()
或.exception()
从done
中获取结果。
- 你需要自己用
- 需要 Task/Future
- 传入的协程会被包装成 Task,否则它不会被调度运行(官方推荐先
create_task
)。
- 传入的协程会被包装成 Task,否则它不会被调度运行(官方推荐先
- 返回值
重要参数,
asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)
- aws:可等待对象集合(Task/Future)。
- timeout:等待的秒数(超时后返回当前完成/未完成任务)。
- return_when:控制何时返回,取值:
ALL_COMPLETED
(默认):全部完成才返回。FIRST_COMPLETED
:任意一个完成就返回。FIRST_EXCEPTION
:任意一个抛异常就返回。
等待带超时的任务:asyncio.wait_for
wait_for
就像在异步任务外面套了一个“倒计时炸弹”,如果任务在规定时间内没完成,就会抛asyncio.TimeoutError
,并(可选地)取消这个任务。
1 | # 一、 |
超时会抛出
asyncio.TimeoutError
参数,
asyncio.wait_for(aw, timeout)
aw
- 可等待对象(协程、Task、Future)。
timeout
- 超时时间(秒,浮点数或整数)。
None
表示无限等待(等价于await aw
)。
执行行为
任务取消
- 默认:一旦超时,会调用
aw.cancel()
取消任务。 - 如果任务忽略取消(
try: except asyncio.CancelledError:
),它可能会继续运行(后台执行)。
- 默认:一旦超时,会调用
异常处理
- 超时时会抛
asyncio.TimeoutError
。
- 超时时会抛
配合 gather/wait 使用
- 可以对多个任务的整体执行时间加总超时限制。
等待异步生成器(async for
)
在
async def
协程函数中,按异步方式遍历一个异步可迭代对象(asynchronous iterable),在每次取下一个元素时都可以执行异步等待(await
),不会阻塞事件循环。换句话说,它是for
循环在异步世界的版本。async for
是“可等待的 for 循环”,专门用来遍历异步数据源,让每次取值的过程都不阻塞事件循环。
1 | # 普通的 for 循环要求迭代器是同步的,取下一个元素是立刻完成的。 |
async for
内部隐式使用await
来获取下一个值flowchart TD A[async for 开始] --> B[调用 __aiter__] B --> C[调用 __anext__] C --> D[await 等待结果] D --> E[返回一个元素] E -->|有值| C E -->|StopAsyncIteration| F[结束循环]
等待异步上下文管理器(async with
)
在异步环境(
async def
协程)中,安全、优雅地管理需要await
的资源获取和释放过程,就像with
是同步版本的资源管理器一样。async with
是协程世界里的with
,专门用来管理需要异步初始化和清理的资源,保证即使出错也会正确释放。
1 | # 在异步场景中,有时进入/退出都需要等待,比如: |
async with
用于异步资源管理,如文件、连接池等flowchart TD A[开始 async with] --> B[进入上下文] B -->|await| C[获取变量] C --> D[执行代码块] D --> E[退出上下文] E -->|await| F[清理资源]
# 进入上下文:调用 __aenter__ # 退出上下文:调用 __aexit__
注意事项
await
只能用于async def
中,不能在普通函数或模块级别使用- 被
await
的对象必须是awaitable
(协程、Future、Task、异步生成器等) await
会让出事件循环控制权,避免阻塞