Python Async
Python Async
Asyncio gives one thread the throughput of hundreds of connections, but only if nothing on the event loop ever blocks - a single stray time.sleep or requests.get freezes every coroutine in the process. The costly mistakes this skill prevents are the silent ones: code that runs correctly but sequentially (awaiting in a loop), tasks that vanish mid-flight (un-referenced fire-and-forget), and a "fast" service whose p99 collapses because one handler does CPU work on the loop.
Mental model
One event loop runs many coroutines cooperatively on a single thread. await is the only place control can switch; between awaits, your code has an exclusive lock on the whole process. Concurrency comes from running tasks, not from awaiting sequentially - await a(); await b() is exactly as slow as sync code.
Operating procedure
Step 1: Gather inputs
- Python version - 3.11+ unlocks
TaskGroupandasyncio.timeout; below that, usegatherandwait_for. - The workload type per operation: network I/O (async-friendly), disk/blocking-library I/O (needs
to_thread), or CPU-bound (needs a process pool). - Every library on the I/O path, checked for an async API:
httpx/aiohttpnotrequests,asyncpgnotpsycopg2,asyncio.sleepnottime.sleep. If unsure whether a call blocks, treat it as blocking. - The concurrency target and the downstream limits (DB pool size, API rate limits).