news 2026/2/4 10:41:17

async 和 await

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
async 和 await

现代版本的 Python 有一种非常直观的方式来定义异步代码。这使它看起来就像正常的"顺序"代码,并在适当的时候"等待"。

当有一个操作需要等待才能给出结果,且支持这个新的 Python 特性时,你可以编写如下代码:

burgers = await get_burgers(2)

这里的关键是await。它告诉 Python 它必须等待get_burgers(2)完成它的工作 ,然后将结果存储在burgers中。这样,Python 就会知道此时它可以去做其他事情 (比如接收另一个请求)。

要使await工作,它必须位于支持这种异步机制的函数内。因此,只需使用async def声明它:

async def get_burgers(number: int): # Do some asynchronous stuff to create the burgers return burgers

...而不是def:

# This is not asynchronous def get_sequential_burgers(number: int): # Do some sequential stuff to create the burgers return burgers

使用async def,Python 就知道在该函数中,它将遇上await,并且它可以"暂停" 执行该函数,直至执行其他操作 后回来。

当你想调用一个async def函数时,你必须"等待"它。因此,这不会起作用:

# This won't work, because get_burgers was defined with: async def burgers = get_burgers(2)

因此,如果你使用的库告诉你可以使用await调用它,则需要使用async def创建路径操作函数 ,如:

@app.get('/burgers') async def read_burgers(): burgers = await get_burgers(2) return burgers

更多技术细节

你可能已经注意到,await只能在async def定义的函数内部使用。

但与此同时,必须"等待"通过async def定义的函数。因此,带async def的函数也只能在async def定义的函数内部调用。

那么,这关于先有鸡还是先有蛋的问题,如何调用第一个async函数?

如果你使用FastAPI,你不必担心这一点,因为"第一个"函数将是你的路径操作函数,FastAPI 将知道如何做正确的事情。

但如果你想在没有 FastAPI 的情况下使用async/await,则可以这样做。

编写自己的异步代码

Starlette (和FastAPI) 是基于 AnyIO 实现的,这使得它们可以兼容 Python 的标准库 asyncio 和 Trio。

特别是,你可以直接使用 AnyIO 来处理高级的并发用例,这些用例需要在自己的代码中使用更高级的模式。

即使你没有使用FastAPI,你也可以使用 AnyIO 编写自己的异步程序,使其拥有较高的兼容性并获得一些好处(例如, 结构化并发)。

我(指原作者 —— 译者注)基于 AnyIO 新建了一个库,作为一个轻量级的封装层,用来优化类型注解,同时提供了更好的自动补全内联错误提示等功能。这个库还附带了一个友好的入门指南和教程,能帮助你理解并编写自己的异步代码:Asyncer。如果你有结合使用异步代码和常规(阻塞/同步)代码的需求,这个库会特别有用。

其他形式的异步代码

这种使用asyncawait的风格在语言中相对较新。

但它使处理异步代码变得容易很多。

这种相同的语法(或几乎相同)最近也包含在现代版本的 JavaScript 中(在浏览器和 NodeJS 中)。

但在此之前,处理异步代码非常复杂和困难。

在以前版本的 Python,你可以使用多线程或者 Gevent。但代码的理解、调试和思考都要复杂许多。

在以前版本的 NodeJS / 浏览器 JavaScript 中,你会使用"回调",因此也可能导致“回调地狱”。

协程

协程只是async def函数返回的一个非常奇特的东西的称呼。Python 知道它有点像一个函数,它可以启动,也会在某个时刻结束,而且它可能会在内部暂停 ⏸ ,只要内部有一个await

通过使用asyncawait的异步代码的所有功能大多数被概括为"协程"。它可以与 Go 的主要关键特性 "Goroutines" 相媲美。

结论

让我们再来回顾下上文所说的:

Python 的现代版本可以通过使用asyncawait语法创建协程,并用于支持异步代码

现在应该能明白其含义了。

所有这些使得 FastAPI(通过 Starlette)如此强大,也是它拥有如此令人印象深刻的性能的原因。

路径操作函数

当你使用def而不是async def来声明一个路径操作函数时,它运行在外部的线程池中并等待其结果,而不是直接调用(因为它会阻塞服务器)。

如果你使用过另一个不以上述方式工作的异步框架,并且你习惯于用普通的def定义普通的仅计算路径操作函数,以获得微小的性能增益(大约100纳秒),请注意,在 FastAPI 中,效果将完全相反。在这些情况下,最好使用async def,除非路径操作函数内使用执行阻塞 I/O 的代码。

在这两种情况下,与你之前的框架相比,FastAPI可能仍然很快。

依赖

这同样适用于依赖。如果一个依赖是标准的def函数而不是async def,它将被运行在外部线程池中。

子依赖

你可以拥有多个相互依赖的依赖以及子依赖 (作为函数的参数),它们中的一些可能是通过async def声明,也可能是通过def声明。它们仍然可以正常工作,这些通过def声明的函数将会在外部线程中调用(来自线程池),而不是"被等待"。

其他函数

你可直接调用通过defasync def创建的任何其他函数,FastAPI 不会影响你调用它们的方式。

这与 FastAPI 为你调用路径操作函数和依赖项的逻辑相反。

如果你的函数是通过def声明的,它将被直接调用(在代码中编写的地方),而不会在线程池中,如果这个函数通过async def声明,当在代码中调用时,你就应该使用await等待函数的结果。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/24 9:55:16

5分钟搞定Android投屏:QtScrcpy零门槛操作指南

还在为手机屏幕太小而烦恼?想要在电脑上流畅操作Android应用?QtScrcpy这款神器让你轻松实现跨平台投屏控制!无需root权限,连接即用,无论是开发调试还是日常使用都能获得极佳体验。🎯 【免费下载链接】QtScr…

作者头像 李华
网站建设 2026/2/3 13:55:52

Windows 11安装蓝屏终结者:MediaCreationTool.bat实战指南

【免费下载链接】MediaCreationTool.bat Universal MCT wrapper script for all Windows 10/11 versions from 1507 to 21H2! 项目地址: https://gitcode.com/gh_mirrors/me/MediaCreationTool.bat "昨天又蓝屏了?别急,看完这篇你也能成为Win…

作者头像 李华
网站建设 2026/2/3 12:54:00

BGE-Large-zh-v1.5终极指南:快速上手文本嵌入模型部署

BGE-Large-zh-v1.5终极指南:快速上手文本嵌入模型部署 【免费下载链接】bge-large-zh-v1.5 项目地址: https://ai.gitcode.com/hf_mirrors/ai-gitcode/bge-large-zh-v1.5 BGE-Large-zh-v1.5是由北京人工智能研究院开发的高性能中文文本嵌入模型,…

作者头像 李华
网站建设 2026/2/4 4:42:44

windows用户态到内核态

以下是一个驱动层 用户态的交互示例,覆盖“超时设置设备状态查询数据读写”全流程,包含驱动代码、用户态头文件、用户态调用代码。 一、整体架构说明层级核心文件/功能驱动层实现 IRP_MJ_DEVICE_CONTROL 处理逻辑,响应自定义IOCTL&#xff0…

作者头像 李华