Coroutines and asyncio.run

asyncio · Essentials · 10 min

Every asyncio program starts here: without an entry point and await, nothing runs.

A function defined with async def is a coroutine function. Calling it does not run its body; it returns a coroutine object, a paused computation waiting to be driven. Something has to run it, and that something is the event loop.

asyncio.run(main()) creates a fresh event loop, runs the coroutine main() to completion, closes the loop and returns the result. Call it once, at the top of your program; inside async code you never call it again.

Inside a coroutine, await expr runs another awaitable and gives you its result. While the awaited thing is waiting (a timer, a socket, a subprocess), the event loop is free to run other code. That is the whole trick: concurrency comes from coroutines voluntarily pausing at await points, all on one thread.

  • await only works inside async def (or the asyncio REPL, python -m asyncio).
  • Awaitables are coroutines, Tasks and Futures.
  • Calling a coroutine function without awaiting it creates an object that never runs, and Python warns "coroutine ... was never awaited".

Async code is "colored": an async function can call sync functions freely, but a sync function cannot await. That is why asyncio programs have a single asyncio.run() at the edge and async all the way down from there.

Example

import asyncio

async def greet(name: str) -> str:
    await asyncio.sleep(0.1)          # pause; the loop could run other work
    return f"hello, {name}"

async def main() -> None:
    coro = greet("ada")
    print(type(coro).__name__)        # coroutine  (nothing has run yet)
    print(await coro)                 # hello, ada

result = asyncio.run(main())
print(result)                         # None

Remember

  • Calling an async def function only creates a coroutine object
  • await runs an awaitable and yields control while it waits
  • asyncio.run() is the single sync-to-async entry point

Watch out for

  • Forgetting await: the coroutine never runs and you get a RuntimeWarning instead of a result.

Check yourself

  1. What does calling an async def function, f(), return without await?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/asyncio-task.html#coroutines