Async Python: asyncio Crash Course
Part 14 of the Python for Everyone track. Last updated: September 2026.
asyncio lets one Python process juggle thousands of I/O-bound tasks — API calls, database queries, file reads — without threads. The mental model is small: async def, await, and an event loop that switches tasks while they wait.
Your first coroutine
import asyncio
async def greet(): # async def makes a COROUTINE function
await asyncio.sleep(1) # await: pause here, let others run
return "hello"
async def main():
result = await greet() # await runs the coroutine to completion
print(result)
asyncio.run(main()) # entry point: runs the event loop
asyncio.run() is the standard entry point — it creates the event loop, runs your coroutine, and closes everything cleanly.
Concurrency with gather
import asyncio, time
async def fetch(site):
await asyncio.sleep(1) # pretend this is a network call
return f"data from {site}"
async def main():
start = time.perf_counter()
# run all three CONCURRENTLY — total time ≈ 1s, not 3s
results = await asyncio.gather(
fetch("a.com"), fetch("b.com"), fetch("c.com")
)
print(results, f"took {time.perf_counter() - start:.1f}s")
asyncio.run(main())
asyncio.gather() is the workhorse: hand it coroutines, get all results back in order. Three 1-second waits finish in ~1 second total.
Timeouts and tasks
import asyncio
async def slow():
await asyncio.sleep(10)
async def main():
try:
await asyncio.wait_for(slow(), timeout=2) # cancel if too slow
except asyncio.TimeoutError:
print("timed out!")
task = asyncio.create_task(slow()) # schedule in the background
await asyncio.sleep(0.1)
task.cancel() # stop it early
print("cancelled:", task.cancelled())
asyncio.run(main())
When NOT to use asyncio
- CPU-bound work (number crunching, image processing) — asyncio won't parallelize it; the GIL still applies. Use multiprocessing instead.
- Simple scripts with one I/O call — plain synchronous code is clearer.
- Libraries without async support can't be awaited — check for async APIs (httpx, aiosqlite, asyncpg).
Key takeaways
- async def defines a coroutine; await pauses it while I/O happens.
- asyncio.run(main()) is the entry point; asyncio.gather(...) runs things concurrently.
- Use wait_for for timeouts and create_task for background work.
- asyncio shines for I/O-bound workloads — not CPU-bound ones.
You've completed the Python for Everyone core track! All 14 posts are now live under the Python label. Next up on the roadmap: Python for Data Science.
Comments
Post a Comment