Backend6 min read

Python Asyncio

Write concurrent Python code with asyncio - async/await syntax, event loop, tasks, gathering, timeouts, and async HTTP with aiohttp.

AsyncioPythonAsyncConcurrencyaiohttp

skill: Python Asyncio category: Backend tags: [Asyncio, Python, Async, Concurrency, aiohttp] readingTime: 6

Asyncio is the right tool for I/O-bound concurrency - making 100 API calls concurrently instead of sequentially. For CPU-bound work, use multiprocessing instead - asyncio runs on a single thread and provides no CPU parallelism. The mental model: async/await is cooperative multitasking - a coroutine yields control when waiting for I/O.

Workflow

  1. Understand async/await basicsasync def defines a coroutine - a function that can pause at await points and let other coroutines run while waiting.
  2. Run tasks concurrently with gatherSequential awaits run one at a time. asyncio.gather runs multiple coroutines concurrently - all I/O waits happen in parallel.
  3. Add timeouts and error handlingAsync I/O that never resolves hangs forever without timeouts. Use asyncio.timeout (Python 3.11+) or asyncio.wait_for.
  4. Control concurrency with semaphoresasyncio.gather with 10,000 tasks can overwhelm the target server or exhaust file descriptors. Semaphores cap concurrent operations.
  5. Integrate async with sync codeCalling async from sync code (e.g., Django views) or sync from async code requires bridging the event loops correctly.

Output Format

Asyncio patterns: async/await, gather for concurrency, Semaphore for rate limiting, asyncio.timeout, and run_in_executor for sync code.

async def main():
    sem = asyncio.Semaphore(20)
    async with aiohttp.ClientSession() as session:
        tasks = [limited_fetch(sem, session, url) for url in urls]
        results = await asyncio.gather(*tasks, return_exceptions=True)

asyncio.run(main())

Examples

Input

Output

Edge Cases & Gotchas

  • Blocking sync code in async context - time.sleep() in an async function blocks the entire event loop. Use await asyncio.sleep() or run_in_executor for blocking calls.
  • Shared mutable state - asyncio runs coroutines on a single thread - no GIL issues. But coroutines that interleave writes to shared state can corrupt it. Use asyncio.Lock.
  • Unclosed client sessions - aiohttp.ClientSession not closed in a finally block leaks connections. Always use async with for session management.
  • Cancellation handling - When a task is cancelled (asyncio.CancelledError), cleanup code in finally blocks must be non-blocking async operations.

When to Break These Rules

  • Use multiprocessing.Pool for CPU-bound parallelism - asyncio adds zero benefit for computation-heavy code.
  • Use Trio instead of asyncio for stricter structured concurrency guarantees - Trio's nursery model prevents "fire and forget" task leaks.

Reference Files

  • https://docs.python.org/3/library/asyncio.html
  • https://docs.aiohttp.org/

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.