Recipe / Python

Python asyncio primer

A short, practical walk through Python's asyncio module — event loops, coroutines, and structured concurrency. Use this as a launching pad when wiring high-throughput Meridian workers that fan out network calls without blocking the main thread.

1Coroutines, not threads

A coroutine is a function declared with async def. Calling it returns a coroutine object — it does not run yet. The event loop schedules execution, and await hands control back so other tasks can progress while you wait on I/O.

2Gather fans out, awaits all

asyncio.gather schedules multiple coroutines concurrently and resolves to the list of their results in input order. Use it when you have a known, bounded set of awaitables — e.g. three HTTP fetches that should overlap rather than serialize.

3Run with asyncio.run

asyncio.run(main()) is the canonical entrypoint — it creates a fresh loop, runs the coroutine to completion, and tears the loop down cleanly. Do not call it from inside another running loop; use await there.

import asyncio

async def fetch(name: str, delay: float) -> str:
    await asyncio.sleep(delay)
    return f"{name} done after {delay}s"

async def main() -> None:
    results = await asyncio.gather(
        fetch("alpha", 0.4),
        fetch("beta", 0.2),
        fetch("gamma", 0.6),
    )
    for r in results:
        print(r)

if __name__ == "__main__":
    asyncio.run(main())