GGistDev

Async Programming in Python

Async IO lets you handle many I/O‑bound operations concurrently using async/await and an event loop.

Coroutines and the event loop

Define coroutines with async def and suspend with await. Use asyncio.run() to execute a top‑level coroutine.

import asyncio

async def fetch(n: int) -> str:
    await asyncio.sleep(0.1)  # simulate I/O
    return f"result-{n}"

async def main() -> None:
    print(await fetch(1))

asyncio.run(main())

Concurrency with tasks

Create tasks to run coroutines concurrently; gather waits for all and returns results.

async def main():
    tasks = [asyncio.create_task(fetch(i)) for i in range(3)]
    results = await asyncio.gather(*tasks)
    print(results)

If any task fails, gather propagates the exception (configurable with return_exceptions=True).

Timeouts, cancellation, and shielding

Cancel tasks or apply timeouts to avoid hanging operations.

async def main():
    try:
        async with asyncio.timeout(1.0):
            await fetch(42)
    except TimeoutError:
        print("timed out")

Cancel explicitly:

t = asyncio.create_task(fetch(1))
t.cancel()
try:
    await t
except asyncio.CancelledError:
    ...

Async context managers and iterators

Use async with and async for when working with async resources/streams.

class A:
    async def __aenter__(self): ...
    async def __aexit__(self, exc_type, exc, tb): ...

async def consume(stream):
    async for item in stream:
        ...

Threads and blocking calls

Never block the event loop with CPU‑bound work or blocking I/O. Offload to threads/processes when needed.

# Python 3.9+
await asyncio.to_thread(blocking_func, arg)
# or run_in_executor
loop = asyncio.get_running_loop()
await loop.run_in_executor(None, blocking_func)

Connection limits and backpressure

Use semaphores/queues to limit concurrency and buffer results safely.

sem = asyncio.Semaphore(10)
async def limited(task):
    async with sem:
        return await task

Best practices

  • Prefer asyncio.run() as the program entry point
  • Avoid blocking the loop; use to_thread/executors for blocking work
  • Add timeouts and handle cancellations
  • Bound concurrency with Semaphore/Queue
  • Use libraries with native async support (HTTP, db drivers)

Summary

  • async/await cooperatively schedule I/O‑bound coroutines
  • Use tasks for concurrency; add timeouts and cancellation handling