
Concurrency: Mastering Async and Await
Unlock the full power of your server. Learn how to optimize FastAPI's event loop to handle thousands of concurrent connections with minimal hardware.
Concurrency: Mastering Async and Await
FastAPI is famous for its speed, but that speed doesn't come for free. To get the most out of it, you must understand how Asynchronous Programming works in Python.
In this lesson, we learn how to keep the "Event Loop" moving to handle massive traffic.
1. The Event Loop Metaphor
Imagine a Chef (the CPU) in a kitchen.
- Synchronous: The chef puts a steak on the grill and stands there watching it for 10 minutes, doing nothing else while it cooks.
- Asynchronous: The chef puts a steak on the grill, sets a timer (awaits), and immediately turns around to chop vegetables or wash dishes. When the timer goes off, the chef returns to the steak.
FastAPI is the Chef. The await keyword is the timer.
2. When to use async def vs def
FastAPI is unique because it supports both.
- Use
async def: When you are doing I/O work (Database calls, API calls, file reading). This tells FastAPI: "I'm going to wait for a bit, go handle other users while I'm gone." - Use
def: For CPU-heavy work (Calculating math, processing images). FastAPI will run these in a separate threadpool so they don't block the main event loop.
3. The Danger: Blocking the Loop
If you use a Sync library (like requests or psycopg2) inside an Async function, you "Freeze" the kitchen.
# DON'T DO THIS
@app.get("/slow")
async def slow_route():
import time
time.sleep(5) # The entire server is frozen for 5 seconds!
return {"msg": "done"}
# DO THIS INSTEAD
import asyncio
@app.get("/fast")
async def fast_route():
await asyncio.sleep(5) # The server handles 1,000 other users while waiting
return {"msg": "done"}
4. Concurrency != Parallelism
- Concurrency: Handling many things at once (Interleaving).
- Parallelism: Doing many things at once (using multiple CPU cores). FastAPI achieves high concurrency with one core, but you use tools like Gunicorn to achieve parallelism by running multiple copies of FastAPI.
Visualizing the Event Loop
graph TD
A["User 1 Request"] --> B["Event Loop"]
B --> C["DB Call (Await 100ms)"]
B --> D["User 2 Request"]
D --> E["Cache Call (Await 5ms)"]
E --> F["Return User 2 Response"]
B --> G["DB Returns Data"]
G --> H["Return User 1 Response"]
Summary
await: The most important keyword in modern Python.- I/O Bound: Use
asyncfor things that involve waiting for a network or disk. - CPU Bound: Use standard
deffor things that involve heavy calculation. - Non-Blocking: Never use
time.sleep()in anasync deffunction.
In the next lesson, we’ll look at Caching with Redis, the ultimate speed booster for any API.
Exercise: The Loop Blocker
You are using a library to generate QR codes. The library is synchronous and takes 200ms of CPU time.
Should you define your FastAPI route as async def or def? Why?