09.3 - Asynchronous Programming with asyncio
Theory 30 min Advanced
What is Async Programming?
Async I/O runs multiple tasks concurrently in a single thread by voluntarily yielding control while waiting for I/O:
Thread 1 (sync): ─── Wait ─── ─── Wait ─── ─── Wait ───
task1 task2 task3
Event loop (async): ─work─ yield ─work─ yield ─work─ yield
task1 →wait task2 →wait task3 →wait
←done ←done ←done
Core Concepts
import asyncio
# A coroutine function (does not run when called)
async def greet(name, delay):
await asyncio.sleep(delay) # non-blocking wait
print(f"Hello, {name}!")
# Run a coroutine
asyncio.run(greet("Alice", 1))
# Run multiple concurrently with gather
async def main():
# All 3 run concurrently — total ~2s, not 6s!
await asyncio.gather(
greet("Alice", 2),
greet("Bob", 1),
greet("Charlie", 1.5),
)
asyncio.run(main())
async for and async with
import asyncio
import aiohttp # pip install aiohttp
# Async context manager
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
# Async generator
async def paginate(url, pages=3):
async with aiohttp.ClientSession() as session:
for page in range(1, pages + 1):
async with session.get(f"{url}?page={page}") as r:
yield await r.json()
async def main():
async for data in paginate("https://api.example.com/items"):
print(data)
asyncio.gather vs asyncio.create_task
async def main():
# gather — concurrent, waits for all
results = await asyncio.gather(
fetch("https://api1.com"),
fetch("https://api2.com"),
return_exceptions=True # don't fail all if one fails
)
# create_task — schedule independently
task1 = asyncio.create_task(fetch("api1.com"))
task2 = asyncio.create_task(fetch("api2.com"))
# ... do other things ...
result1 = await task1
result2 = await task2
# asyncio.as_completed — process results as they arrive
for coro in asyncio.as_completed([fetch(url) for url in urls]):
result = await coro
process(result)
When to Use asyncio vs threading vs multiprocessing
| Workload | Best tool |
|---|---|
| Many I/O operations (APIs, DB) | asyncio |
| Mixed I/O with blocking libraries | threading |
| CPU-intensive computation | multiprocessing |
| Simple scripts | threading (simpler) |
Key Vocabulary
| Term | Definition |
|---|---|
| Coroutine | Function defined with async def — doesn't run until awaited |
await | Suspend current coroutine until the awaitable completes |
| Event loop | Central dispatcher that runs and schedules coroutines |
asyncio.run() | Entry point — creates event loop and runs a coroutine |
asyncio.gather() | Run multiple coroutines concurrently and collect results |
asyncio.create_task() | Schedule a coroutine as an independent task |
aiohttp | Async HTTP client/server library |
Summary
async defdefines a coroutine;awaitpauses it until an async operation completesasyncio.run(coro)is the entry point for async programsasyncio.gather(*coros)runs multiple coroutines concurrently in a single thread- Use
asynciofor I/O-bound work with many concurrent operations aiohttpprovides async HTTP — ideal for calling multiple APIs in parallel
📄️ 09.1 - Generators & Iterators
Write memory-efficient code with Python iterators, generator functions, generator expressions, and itertools
📄️ 09.2 - Threading & Multiprocessing
Run code concurrently with threading (I/O-bound) and multiprocessing (CPU-bound), understand the GIL, and use concurrent.futures
📄️ 09.3 - Async/Await
Write non-blocking Python code with async/await, coroutines, asyncio.gather, and aiohttp for async HTTP
📄️ Lab - Module 09
Build a concurrent URL checker using asyncio, generators, and itertools
📄️ Quiz - Module 09
30 questions on generators, iterators, threading, multiprocessing, and asyncio