Introduced in Python 3.5 (2015) as part of PEP 492.
Inspired by JavaScript's async/await model.
Built on top of coroutines and the asyncio event loop.
async defines a coroutine function (a special kind of function that can pause and resume).
await is used inside an async function to pause execution until the awaited task completes.
To write non-blocking, concurrent code.
Ideal for I/O-bound tasks like:
Web requests
File operations
Database queries
This imply run a main function which waits for the say_hello() funciton to finish and then carry on.
The async functions must be run from an event loop context provided by asyncio. Run the code with python.exe example.py or from VS Code.
import asyncio
async def say_hello():
print("Hello...")
async def main():
print("main starts...")
await say_hello()
print("...main ended!")
asyncio.run(await main())
If running the code from Jupyter notebook, it would raise an error about "cannot be called from a running event loop" because Jupyter notebook itself is already in a event loop. Therefore, only need to "await main()" to run the function and no need for the aysncio. This is the same for other frameworks(e.g. fastapi) that are already in an event loop.
How about runing the main() directly? i.e. run "main()" without the " await " or " asyncio.run(await..". It would say "RuntimeWarning: coroutine 'main' was never awaited main()" because it simply defined the coroutine main() instead of actually running it. It has to use await or run to explicitly run the function.
Running await from without an event loop / coroutine function context, gives an error "SyntaxError: 'await' outside function". For example, running "await main()" from a python.exe example.py context, but note again running "await main()" from jupyter notebook / fastapi is ok.
await must be inside an async def function, or a event loop context already (e.g. jupyter notebook , fastapi).
And that function must be run by an event loop (like asyncio.run()).
Python’s built-in asynchronous I/O framework.
Manages the event loop, tasks, and coroutines.
import asyncio
async def task(name, delay):
print(f"Start {name}")
await asyncio.sleep(delay)
print(f"End {name}")
async def main():
await asyncio.gather(
task("A", 2),
task("B", 1)
)
asyncio.run(main())
from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.get("/wait")
async def wait():
await asyncio.sleep(2)
return {"message": "Done waiting!"}
Then "uvicorn example:app --port 8050" to start the fast api app in example.py
Or "hypercorn example:app --bind 0.0.0.0:8050"
The async and await are used in the route /wait
Note Flask is synchronous and doesn't naturally support async. it needs the Flask async extra to work.
"pip install "flask[async]"
from flask import Flask
import asyncio
app = Flask(__name__)
async def hello():
await asyncio.sleep(1)
print('hello')
@app.route("/async")
async def async_route():
await hello()
return "Async in Flask!"
NOTE Flask supports async routes, but does NOT run them concurrently, unless you use an ASGI server like Hypercorn.
NOTE Flask is still a WSGI app so some ASGI server like Uvicorn is not compatible with it, however, Hypercorn is compatible.
Running the flask app with the "app.run()" in the script or from a WSGI host won't support the actual async, it has to be run from Hypercorn or similar.
hypercorn test_concurrent:app --bind 0.0.0.0:8050
Not really supported, but there is workaround
Dash does allow async def callbacks syntactically since version 2.6.0 and Internally, Dash should detect and await them
However, Dash does not actually await the coroutine returned by those callbacks.
As a result, the callback returns a coroutine object, which leads to the error:
TypeError: Object of type coroutine is not JSON serializable
The reason is Dash is built on Flask (WSGI), and even though it allows async def, the callback execution engine is synchronous. It does not detect or await coroutines currently — it just calls the function and expects a return value.
A workaround is consider it as a Flask app, and use asyncio to run the callback logic within the callback, but the callback itself is still synchronous.
import asyncio
from dash import Dash, html, dcc, Input, Output
app = Dash(__name__)
server = app.server
app.layout = html.Div([
dcc.Input(id='input', value='Hello', type='text'),
html.Div(id='output')
])
async def hello(value):
return f"hello {value}"
@app.callback(
Output('output', 'children'),
Input('input', 'value')
)
def update_output(value):
return asyncio.run(hello(value))
Note here the callback function update_output is not defined as async. Putting an async won't break the syntax, but the callbacked is simply run instead of being awaited, so it returned the coroutine definition of update_output() instead of the "hello xxx" message.
Again use Hypercorn to be compatible with the Flask instance (dash.server) to run the app in example.py
hypercorn example:server --bind 0.0.0.0:8050