Warming up the neural circuits...
By the end of this chapter you will:
Async is not about making CPU math faster. It is about keeping one process productive while waiting on I/O: network calls, database responses, file waits, and throttling.
If your service handles many concurrent waiting operations, async can dramatically improve throughput with predictable resource usage.
import asyncio
async def fetch_user(user_id: int) -> str:
await asyncio.sleep(0.1)
return f"user-{user_id}"async def defines a coroutine. await yields control back to the event loop while waiting.
import asyncio
async def work(i: int) -> int:
await asyncio.sleep(0.2)
return i * 2
async def main():
tasks = [asyncio.create_task(work(i
import asyncio
async def slow_call():
await asyncio.sleep(10)
async def main():
try:
await asyncio.wait_for(slow_call(), timeout=1.0)
except TimeoutError:
print("Bad inside coroutine:
time.sleep(...)Use await asyncio.to_thread(sync_fn, ...) for blocking sync calls.
import asyncio
import time
def blocking():
time.sleep(1)
return "done"
async def main():
result = await asyncio.to_thread(blocking)
print| Workload | Best starting model |
|---|---|
| Many I/O waits, high concurrency | asyncio |
| Mixed code with blocking libs | threads |
| Small scripts/low concurrency | sync |
| CPU-heavy parallel compute | multiprocessing |
Choose based on bottleneck, not trend.
Define timeout, retry, and cancellation behavior before writing business logic. Async failures are mostly policy failures, not syntax failures.
| Mistake | Symptom | Fix |
|---|---|---|
| Calling async function without await | Coroutine object printed, no execution | await it or schedule as task |
Using time.sleep in coroutine | Event loop stalls | Use await asyncio.sleep |
| Missing timeouts on network calls | Hanging requests | Use wait_for or client-level timeouts |
| Fire-and-forget tasks without tracking | Lost exceptions | Keep references and await during shutdown |
| Mixing sync client in async path | Throughput collapse | Use async libraries or to_thread wrappers |
Three tasks each sleeping 1 secondTotal near 1 second concurrentlyCoroutine taking 5 secondsTimeout handled after 1 secondLooping coroutineCancellation handled cleanlyto_thread to call a blocking function from async context.Blocking CPU-lite helperEvent loop stays responsiveAPI fan-out, image processing, simple scriptReasoned model choice for eachBeginner:
"What is the difference between a coroutine and a task?"
A coroutine is awaitable work definition. A task is a scheduled coroutine managed by the event loop.
"Why is time.sleep harmful in async code?"
It blocks the event loop, pausing all concurrent tasks.
Senior:
"How do you design cancellation-safe async services?"
Propagate cancellation, add explicit timeout policy, and clean up resources in
CancelledErrorhandlers.
"When is async the wrong choice?"
For CPU-bound workloads or small systems where async complexity exceeds performance gains.
Need many concurrent waits? -> asyncio
Need timeout policy? -> wait_for/client timeout
Need blocking bridge? -> asyncio.to_thread
Need CPU parallelism? -> multiprocessingWhat is the primary strength of asyncio?
create_task schedules concurrently. gather awaits completion and collects results.
Always define timeout policy for external I/O.