2021-04-07 06:31:28 +08:00
|
|
|
.. _async_await:
|
|
|
|
|
|
|
|
Using ``async`` and ``await``
|
|
|
|
=============================
|
|
|
|
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
|
|
|
|
Routes, error handlers, before request, after request, and teardown
|
|
|
|
functions can all be coroutine functions if Flask is installed with the
|
2021-11-12 08:12:08 +08:00
|
|
|
``async`` extra (``pip install flask[async]``). This allows views to be
|
2021-04-07 06:31:28 +08:00
|
|
|
defined with ``async def`` and use ``await``.
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
@app.route("/get-data")
|
|
|
|
async def get_data():
|
|
|
|
data = await async_db_query(...)
|
|
|
|
return jsonify(data)
|
|
|
|
|
2021-05-28 07:01:48 +08:00
|
|
|
Pluggable class-based views also support handlers that are implemented as
|
|
|
|
coroutines. This applies to the :meth:`~flask.views.View.dispatch_request`
|
|
|
|
method in views that inherit from the :class:`flask.views.View` class, as
|
|
|
|
well as all the HTTP method handlers in views that inherit from the
|
|
|
|
:class:`flask.views.MethodView` class.
|
|
|
|
|
2021-11-12 08:12:08 +08:00
|
|
|
.. admonition:: Using ``async`` with greenlet
|
|
|
|
|
|
|
|
When using gevent or eventlet to serve an application or patch the
|
|
|
|
runtime, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
|
|
|
required.
|
|
|
|
|
2021-04-07 06:31:28 +08:00
|
|
|
|
|
|
|
Performance
|
|
|
|
-----------
|
|
|
|
|
|
|
|
Async functions require an event loop to run. Flask, as a WSGI
|
|
|
|
application, uses one worker to handle one request/response cycle.
|
|
|
|
When a request comes in to an async view, Flask will start an event loop
|
|
|
|
in a thread, run the view function there, then return the result.
|
|
|
|
|
|
|
|
Each request still ties up one worker, even for async views. The upside
|
|
|
|
is that you can run async code within a view, for example to make
|
|
|
|
multiple concurrent database queries, HTTP requests to an external API,
|
|
|
|
etc. However, the number of requests your application can handle at one
|
|
|
|
time will remain the same.
|
|
|
|
|
|
|
|
**Async is not inherently faster than sync code.** Async is beneficial
|
|
|
|
when performing concurrent IO-bound tasks, but will probably not improve
|
|
|
|
CPU-bound tasks. Traditional Flask views will still be appropriate for
|
|
|
|
most use cases, but Flask's async support enables writing and using
|
|
|
|
code that wasn't possible natively before.
|
|
|
|
|
|
|
|
|
2021-04-17 18:49:24 +08:00
|
|
|
Background tasks
|
|
|
|
----------------
|
|
|
|
|
|
|
|
Async functions will run in an event loop until they complete, at
|
|
|
|
which stage the event loop will stop. This means any additional
|
|
|
|
spawned tasks that haven't completed when the async function completes
|
|
|
|
will be cancelled. Therefore you cannot spawn background tasks, for
|
|
|
|
example via ``asyncio.create_task``.
|
|
|
|
|
|
|
|
If you wish to use background tasks it is best to use a task queue to
|
|
|
|
trigger background work, rather than spawn tasks in a view
|
|
|
|
function. With that in mind you can spawn asyncio tasks by serving
|
2021-05-03 04:05:08 +08:00
|
|
|
Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter
|
2022-06-13 04:45:31 +08:00
|
|
|
as described in :doc:`deploying/asgi`. This works as the adapter creates
|
|
|
|
an event loop that runs continually.
|
2021-04-17 18:49:24 +08:00
|
|
|
|
|
|
|
|
2021-04-07 06:31:28 +08:00
|
|
|
When to use Quart instead
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
Flask's async support is less performant than async-first frameworks due
|
|
|
|
to the way it is implemented. If you have a mainly async codebase it
|
|
|
|
would make sense to consider `Quart`_. Quart is a reimplementation of
|
|
|
|
Flask based on the `ASGI`_ standard instead of WSGI. This allows it to
|
|
|
|
handle many concurrent requests, long running requests, and websockets
|
2021-05-03 04:05:08 +08:00
|
|
|
without requiring multiple worker processes or threads.
|
2021-04-07 06:31:28 +08:00
|
|
|
|
|
|
|
It has also already been possible to run Flask with Gevent or Eventlet
|
|
|
|
to get many of the benefits of async request handling. These libraries
|
|
|
|
patch low-level Python functions to accomplish this, whereas ``async``/
|
|
|
|
``await`` and ASGI use standard, modern Python capabilities. Deciding
|
|
|
|
whether you should use Flask, Quart, or something else is ultimately up
|
|
|
|
to understanding the specific needs of your project.
|
|
|
|
|
2022-07-09 01:21:42 +08:00
|
|
|
.. _Quart: https://github.com/pallets/quart
|
2021-04-07 06:31:28 +08:00
|
|
|
.. _ASGI: https://asgi.readthedocs.io/en/latest/
|
|
|
|
|
|
|
|
|
|
|
|
Extensions
|
|
|
|
----------
|
|
|
|
|
2021-05-03 04:05:08 +08:00
|
|
|
Flask extensions predating Flask's async support do not expect async views.
|
|
|
|
If they provide decorators to add functionality to views, those will probably
|
2021-04-07 06:31:28 +08:00
|
|
|
not work with async views because they will not await the function or be
|
|
|
|
awaitable. Other functions they provide will not be awaitable either and
|
|
|
|
will probably be blocking if called within an async view.
|
|
|
|
|
2021-05-03 03:49:46 +08:00
|
|
|
Extension authors can support async functions by utilising the
|
|
|
|
:meth:`flask.Flask.ensure_sync` method. For example, if the extension
|
|
|
|
provides a view function decorator add ``ensure_sync`` before calling
|
|
|
|
the decorated function,
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
def extension(func):
|
|
|
|
@wraps(func)
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
... # Extension logic
|
|
|
|
return current_app.ensure_sync(func)(*args, **kwargs)
|
|
|
|
|
|
|
|
return wrapper
|
|
|
|
|
2021-04-07 06:31:28 +08:00
|
|
|
Check the changelog of the extension you want to use to see if they've
|
|
|
|
implemented async support, or make a feature request or PR to them.
|
|
|
|
|
|
|
|
|
|
|
|
Other event loops
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
At the moment Flask only supports :mod:`asyncio`. It's possible to
|
|
|
|
override :meth:`flask.Flask.ensure_sync` to change how async functions
|
|
|
|
are wrapped to use a different library.
|