Level: Advanced | Prerequisite: Week 4
Learning Goals
- Uses
async/awaitsyntax - Runs concurrently with
asyncio.run,asyncio.gather - Asynchronous HTTP calls (
httpx,aiohttp) - Producer/consumer pattern
1. Coroutine
A function defined with async def is a coroutine. Calling it does not execute immediately but returns an object.
import asyncio
async def hello():
print("hello")
await asyncio.sleep(1)
print("world")
asyncio.run(hello())
2. asyncio.gather
Run multiple coroutines concurrently.
async def task(name, delay):
await asyncio.sleep(delay)
print(f"{name} completed")
async def main():
await asyncio.gather(
task("A", 1),
task("B", 2),
task("C", 1),
)
asyncio.run(main())
#Approximately 2 seconds required (4 seconds for sequential)
3. Asynchronous HTTP
import asyncio
import httpx
async def fetch(client, url):
r = await client.get(url)
return len(r.text)
async def main():
async with httpx.AsyncClient() as client:
tasks = [fetch(client, "https://example.com") for _ in range(10)]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
requests is synchronous, httpx and aiohttp are asynchronous.
4. producer / consumer
async def producer(q):
for i in range(5):
await q.put(i)
print(f"Production: {i}")
await asyncio.sleep(0.1)
await q.put(None) #End signal
async def consumer(q):
while True:
item = await q.get()
if item is None:
break
print(f"Consumption: {item}")
async def main():
q = asyncio.Queue()
await asyncio.gather(producer(q), consumer(q))
5. Timeout
try:
result = await asyncio.wait_for(slow_task(), timeout=1.0)
except asyncio.TimeoutError:
print("Time out")
6. Synchronous vs Asynchronous
| Threading | asyncio | |
|---|---|---|
| Unit | OS thread | Coroutine |
| Context switching | Determined by OS | Only in await |
| Memory | Heavy | Light |
| Library | Mostly compatible | Needs async support |
Key Examples (examples/)
| File | Content |
|---|---|
01_basic.py |
async/await basic |
02_gather.py |
gather concurrent execution |
03_queue.py |
Queue producer/consumer |
04_timeout.py |
wait_for timeout |
⚠️ Common Mistakes
async defresult is not insideawait— only a coroutine object is returned, not executed.awaitinside a synchronous function —awaitcan only be used insideasync def.- Blocking function call — Use
await asyncio.sleepinstead oftime.sleep. - Nested asyncio.run — In environments like Jupyter, there is already a loop. Use
await main().
❓ FAQ
Q1. Can requests be used inside asyncio?
A. It works. However, since it is blocking, there is no concurrency benefit. Use httpx.
Q2. asyncio.gather vs asyncio.wait?
A. gather receives results in order and propagates on failure. wait is more flexible.
Q3. Debugging async functions is difficult.
A. traceback is somewhat complex. PyCharm/VS Code debugger can help.
📝 Exercises (exercises/)
exercise1.md— Measure concurrent fetch timeexercise2.md— Queue-based task distributionexercise3.md— Fetch with a timeout
Next Week
Week 6. Test