Skip to content

Async programming

Mid / Senior

The event loop, coroutines, and where asyncio helps or hurts.

5 questions · answers hidden

  1. 01What actually happens when you `await` a coroutine?

    await suspends the current coroutine and yields control back to the event loop, handing it an awaitable. The event loop registers a callback to resume the coroutine when that awaitable completes (for example, when a socket becomes readable) and is then free to run other ready tasks.

    Key point: await does not create concurrency by itself. await foo() runs foo to completion before the next line, exactly like a synchronous call from the caller’s point of view. Concurrency comes from having multiple tasks scheduled on the loop — created with asyncio.create_task, asyncio.gather, or a TaskGroup — so that while one is suspended on I/O the loop can advance another.

    # Sequential: ~2s total
    await fetch(a)
    await fetch(b)
    
    # Concurrent: ~1s total
    await asyncio.gather(fetch(a), fetch(b))
    
    #q-what-actually-happens-when-you-await-a-coroutine
  2. 02Why can a single blocking call freeze an entire asyncio application?

    There is one event loop thread. If a coroutine runs CPU-bound code or calls a blocking synchronous API (a non-async DB driver, time.sleep, requests.get), it never yields to the loop, so every other task is starved until it returns — including timers, health checks, and cancellation.

    Fixes:

    • Use async-native libraries for I/O (asyncpg, httpx, aiofiles).
    • Push blocking or CPU-bound work off the loop with await asyncio.to_thread(func, ...) (threads, good for blocking I/O) or a ProcessPoolExecutor via loop.run_in_executor (processes, good for CPU work).
    • Keep individual coroutine steps short so the loop stays responsive.
    #q-why-can-a-single-blocking-call-freeze-an-entire-asyncio-appl
  3. 03When is asyncio the wrong tool?
    • CPU-bound workloads. The GIL and the single loop thread mean you get no parallelism; multiprocessing or a native extension is the answer.
    • Simple, low-concurrency scripts. The overhead in complexity (coloured functions, careful cancellation, different libraries) buys nothing if you are making ten HTTP calls in a cron job — a thread pool is simpler.
    • Codebases dominated by mature sync libraries. Mixing paradigms tends to leak blocking calls onto the loop. Threads may integrate more cleanly.

    asyncio shines when you have many concurrent, mostly-idle I/O connections (sockets, websockets, upstream services) and want to hold them cheaply on one thread.

    #q-when-is-asyncio-the-wrong-tool
  4. 04How does cancellation work, and what's the common bug?

    Cancelling a task raises asyncio.CancelledError inside it at the next suspension point. Well-behaved code lets it propagate so finally blocks and context managers can clean up.

    The classic bug is swallowing it:

    try:
        await something()
    except Exception:      # CancelledError is a BaseException in 3.8+, but people still catch broadly
        log.exception("oops")
    

    If you catch CancelledError (directly, or via an over-broad handler on older code paths) and do not re-raise, the task ignores cancellation — timeouts and shutdown hang. If you must run cleanup on cancellation, catch it, do the work, and raise.

    #q-how-does-cancellation-work-and-whats-the-common-bug
  5. 05What problem do `asyncio.TaskGroup` and structured concurrency solve?

    Before TaskGroup (3.11), create_task produced “background” tasks with no owner. If one failed, the exception surfaced late (or only as a “task exception was never retrieved” warning), and it was easy to leak tasks that outlived the scope that started them.

    TaskGroup ties task lifetimes to a lexical scope:

    async with asyncio.TaskGroup() as tg:
        tg.create_task(worker(1))
        tg.create_task(worker(2))
    # all tasks are done here
    

    If any task raises, the group cancels the siblings and propagates the error(s) as an ExceptionGroup once the block exits. Nothing escapes the async with.

    #q-what-problem-do-asynciotaskgroup-and-structured-concurrency-