forked from orbit-oss/flask
update async docs
This commit is contained in:
parent
61fbae8664
commit
dc3e9c0cc3
7 changed files with 109 additions and 65 deletions
81
docs/async-await.rst
Normal file
81
docs/async-await.rst
Normal 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.
|
||||||
|
|
@ -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.
|
|
||||||
|
|
@ -171,16 +171,23 @@ Also see the :doc:`/becomingbig` section of the documentation for some
|
||||||
inspiration for larger applications based on Flask.
|
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
|
Flask supports ``async`` coroutines for view functions by executing the
|
||||||
others by executing the coroutine on a seperate thread instead of
|
coroutine on a separate thread instead of using an event loop on the
|
||||||
utilising an event loop on the main thread as an async first (ASGI)
|
main thread as an async-first (ASGI) framework would. This is necessary
|
||||||
frameworks would. This is necessary for Flask to remain backwards
|
for Flask to remain backwards compatible with extensions and code built
|
||||||
compatibility with extensions and code built before ``async`` was
|
before ``async`` was introduced into Python. This compromise introduces
|
||||||
introduced into Python. This compromise introduces a performance cost
|
a performance cost compared with the ASGI frameworks, due to the
|
||||||
compared with the ASGI frameworks, due to the overhead of the threads.
|
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
|
What Flask is, What Flask is Not
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ instructions for web development with Flask.
|
||||||
patterns/index
|
patterns/index
|
||||||
deploying/index
|
deploying/index
|
||||||
becomingbig
|
becomingbig
|
||||||
async_await
|
async-await
|
||||||
|
|
||||||
|
|
||||||
API Reference
|
API Reference
|
||||||
|
|
|
||||||
|
|
@ -1520,12 +1520,13 @@ class Flask(Scaffold):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def ensure_sync(self, func):
|
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
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
Override if you wish to change how asynchronous functions are
|
|
||||||
run.
|
|
||||||
"""
|
"""
|
||||||
if iscoroutinefunction(func):
|
if iscoroutinefunction(func):
|
||||||
return run_async(func)
|
return run_async(func)
|
||||||
|
|
|
||||||
|
|
@ -475,8 +475,8 @@ class Blueprint(Scaffold):
|
||||||
"""Ensure the function is synchronous.
|
"""Ensure the function is synchronous.
|
||||||
|
|
||||||
Override if you would like custom async to sync behaviour in
|
Override if you would like custom async to sync behaviour in
|
||||||
this blueprint. Otherwise :meth:`~flask.Flask..ensure_sync` is
|
this blueprint. Otherwise the app's
|
||||||
used.
|
:meth:`~flask.Flask.ensure_sync` is used.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -742,9 +742,10 @@ def run_async(func):
|
||||||
"Install Flask with the 'async' extra in order to use async views."
|
"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":
|
if ContextVar.__module__ == "werkzeug.local":
|
||||||
raise RuntimeError(
|
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)
|
@wraps(func)
|
||||||
|
|
@ -763,9 +764,9 @@ def run_async(func):
|
||||||
async def inner(*a, **k):
|
async def inner(*a, **k):
|
||||||
"""This restores the context before awaiting the func.
|
"""This restores the context before awaiting the func.
|
||||||
|
|
||||||
This is required as the func must be awaited within the
|
This is required as the function must be awaited within the
|
||||||
context. Simply calling func (as per the
|
context. Only calling ``func`` (as per the
|
||||||
copy_current_xxx_context functions) doesn't work as the
|
``copy_current_xxx_context`` functions) doesn't work as the
|
||||||
with block will close before the coroutine is awaited.
|
with block will close before the coroutine is awaited.
|
||||||
"""
|
"""
|
||||||
if ctx is not None:
|
if ctx is not None:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue