使用 async 和 await — Flask 文档

来自菜鸟教程
Flask/docs/2.0.x/async-await
跳转至:导航、​搜索

使用 async 和 await

2.0 版中的新功能。


如果 Flask 与 async 额外 (pip install flask[async]) 一起安装,则路由、错误处理程序、请求前、请求后和拆卸函数都可以是协程函数。 它需要 Python 3.7+,其中 contextvars.ContextVar 可用。 这允许使用 async def 定义视图并使用 await

@app.route("/get-data")
async def get_data():
    data = await async_db_query(...)
    return jsonify(data)

可插入的基于类的视图还支持作为协程实现的处理程序。 这适用于继承自 flask.views.View 类的视图中的 dispatch_request() 方法,以及继承自 flask.views.MethodView 类的视图中的所有 HTTP 方法处理程序。

在 Python 3.8 上的 Windows 上使用 async

Python 3.8 有一个与 Windows 上的 asyncio 相关的错误。 如果遇到类似 ValueError: set_wakeup_fd only works in main thread 的问题,请升级到 Python 3.9。


表现

异步函数需要一个事件循环来运行。 Flask 作为 WSGI 应用程序,使用一个 worker 来处理一个请求/响应周期。 当请求进入异步视图时,Flask 将在线程中启动一个事件循环,在那里运行视图函数,然后返回结果。

即使对于异步视图,每个请求仍然会占用一个工作人员。 好处是您可以在视图中运行异步代码,例如进行多个并发数据库查询、对外部 API 的 HTTP 请求等。 但是,您的应用程序一次可以处理的请求数量将保持不变。

异步本质上并不比同步代码快。 异步在执行并发 IO 密集型任务时很有用,但可能不会改善 CPU 密集型任务。 传统的 Flask 视图仍然适用于大多数用例,但 Flask 的异步支持使编写和使用以前无法在本地实现的代码成为可能。


后台任务

异步函数将在事件循环中运行,直到它们完成,在该阶段事件循环将停止。 这意味着异步功能完成时尚未完成的任何其他衍生任务将被取消。 因此,您无法生成后台任务,例如通过 asyncio.create_task

如果您希望使用后台任务,最好使用任务队列来触发后台工作,而不是在视图函数中生成任务。 考虑到这一点,您可以通过为 Flask 提供 ASGI 服务器并使用 asgiref WsgiToAsgi 适配器来生成异步任务,如 ASGI 中所述。 这是因为适配器创建了一个持续运行的事件循环。


何时使用 Quart 代替

由于其实现方式,Flask 的异步支持的性能不如异步优先框架。 如果您有一个主要是异步代码库,那么考虑 Quart 是有意义的。 Quart 是基于 ASGI 标准而不是 WSGI 的 Flask 的重新实现。 这允许它处理许多并发请求、长时间运行的请求和 websocket,而不需要多个工作进程或线程。

也已经可以使用 Gevent 或 Eventlet 运行 Flask 以获得异步请求处理的许多好处。 这些库修补低级 Python 函数来实现这一点,而 async/await 和 ASGI 使用标准的现代 Python 功能。 决定是否应该使用 Flask、Quart 或其他东西最终取决于了解项目的特定需求。


插件

在 Flask 的异步支持之前的 Flask 扩展不期望异步视图。 如果它们提供装饰器来为视图添加功能,那么这些可能无法与异步视图一起使用,因为它们不会等待函数或可等待。 他们提供的其他功能也不会等待,如果在异步视图中调用,可能会阻塞。

扩展作者可以通过使用 flask.Flask.ensure_sync() 方法来支持异步功能。 例如,如果扩展提供了一个视图函数装饰器,在调用被装饰的函数之前添加 ensure_sync

def extension(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ...  # Extension logic
        return current_app.ensure_sync(func)(*args, **kwargs)

    return wrapper

检查您要使用的扩展的更新日志,看看他们是否实现了异步支持,或者向他们提出功能请求或 PR。


其他事件循环

目前 Flask 仅支持 [X34X]。 可以覆盖 flask.Flask.ensure_sync() 以更改异步函数的包装方式以使用不同的库。