update async docs

This commit is contained in:
David Lord 2021-04-06 15:31:28 -07:00
parent 61fbae8664
commit dc3e9c0cc3
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
7 changed files with 109 additions and 65 deletions

81
docs/async-await.rst Normal file
View File

@ -0,0 +1,81 @@
.. _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
``async`` extra (``pip install flask[async]``). This allows views to be
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)
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.
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
without requiring individual worker processes or threads.
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.
.. _Quart: https://gitlab.com/pgjones/quart
.. _ASGI: https://asgi.readthedocs.io/en/latest/
Extensions
----------
Existing Flask extensions only expect views to be synchronous. If they
provide decorators to add functionality to views, those will probably
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.
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.

View File

@ -1,46 +0,0 @@
.. _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 ``async`` extra (``pip install flask[async]``). This allows code
such as,
.. code-block:: python
@app.route("/")
async def index():
return await ...
including the usage of any asyncio based libraries.
When to use Quart instead
-------------------------
Flask's ``async/await`` support is less performant than async first
frameworks due to the way it is implemented. Therefore if you have a
mainly async codebase it would make sense to consider `Quart
<https://gitlab.com/pgjones/quart>`_. Quart is a reimplementation of
the Flask using ``async/await`` based on the ASGI standard (Flask is
based on the WSGI standard).
Decorators
----------
Decorators designed for Flask, such as those in Flask extensions are
unlikely to work. This is because the decorator will not await the
coroutine function nor will they themselves be awaitable.
Other event loops
-----------------
At the moment Flask only supports asyncio - the
:meth:`flask.Flask.ensure_sync` should be overridden to support
alternative event loops.

View File

@ -171,16 +171,23 @@ Also see the :doc:`/becomingbig` section of the documentation for some
inspiration for larger applications based on Flask.
Async-await and ASGI support
Async/await and ASGI support
----------------------------
Flask supports ``async`` coroutines for view functions, and certain
others by executing the coroutine on a seperate thread instead of
utilising an event loop on the main thread as an async first (ASGI)
frameworks would. This is necessary for Flask to remain backwards
compatibility with extensions and code built before ``async`` was
introduced into Python. This compromise introduces a performance cost
compared with the ASGI frameworks, due to the overhead of the threads.
Flask supports ``async`` coroutines for view functions by executing the
coroutine on a separate thread instead of using an event loop on the
main thread as an async-first (ASGI) framework would. This is necessary
for Flask to remain backwards compatible with extensions and code built
before ``async`` was introduced into Python. This compromise introduces
a performance cost compared with the ASGI frameworks, due to the
overhead of the threads.
Due to how tied to WSGI Flask's code is, it's not clear if it's possible
to make the ``Flask`` class support ASGI and WSGI at the same time. Work
is currently being done in Werkzeug to work with ASGI, which may
eventually enable support in Flask as well.
See :doc:`/async-await` for more discussion.
What Flask is, What Flask is Not

View File

@ -59,7 +59,7 @@ instructions for web development with Flask.
patterns/index
deploying/index
becomingbig
async_await
async-await
API Reference

View File

@ -1520,12 +1520,13 @@ class Flask(Scaffold):
return False
def ensure_sync(self, func):
"""Ensure that the returned function is sync and calls the async func.
"""Ensure that the function is synchronous for WSGI workers.
Plain ``def`` functions are returned as-is. ``async def``
functions are wrapped to run and wait for the response.
Override this method to change how the app runs async views.
.. versionadded:: 2.0
Override if you wish to change how asynchronous functions are
run.
"""
if iscoroutinefunction(func):
return run_async(func)

View File

@ -475,8 +475,8 @@ class Blueprint(Scaffold):
"""Ensure the function is synchronous.
Override if you would like custom async to sync behaviour in
this blueprint. Otherwise :meth:`~flask.Flask..ensure_sync` is
used.
this blueprint. Otherwise the app's
:meth:`~flask.Flask.ensure_sync` is used.
.. versionadded:: 2.0
"""

View File

@ -742,9 +742,10 @@ def run_async(func):
"Install Flask with the 'async' extra in order to use async views."
)
# Check that Werkzeug isn't using its fallback ContextVar class.
if ContextVar.__module__ == "werkzeug.local":
raise RuntimeError(
"async cannot be used with this combination of Python & Greenlet versions"
"Async cannot be used with this combination of Python & Greenlet versions."
)
@wraps(func)
@ -763,9 +764,9 @@ def run_async(func):
async def inner(*a, **k):
"""This restores the context before awaiting the func.
This is required as the func must be awaited within the
context. Simply calling func (as per the
copy_current_xxx_context functions) doesn't work as the
This is required as the function must be awaited within the
context. Only calling ``func`` (as per the
``copy_current_xxx_context`` functions) doesn't work as the
with block will close before the coroutine is awaited.
"""
if ctx is not None: