FASTAPI / 8. ASYNC & BACKGROUND TASKS
Async Python & Background Tasks
Non-blocking I/O and fire-and-forget tasks that don't block responses
EXPLANATION
FastAPI is built on asyncio — Python's async I/O framework. Understanding async is critical for writing performant FastAPI code. The core insight: when your code does I/O (DB query, HTTP call, file read), the CPU is idle waiting. In synchronous code, this blocks the entire thread. In async code, you yield control back to the event loop so it can process other requests while you wait. async/await: • async def → defines a coroutine (a function that can be paused) • await → pauses the coroutine until the awaited thing completes, without blocking the thread • You must await only async functions from async-compatible libraries When to use async vs sync in FastAPI: • Use async def when calling async libraries (httpx, asyncpg, aiofiles, aioredis) • Use def (sync) when calling sync libraries (requests, psycopg2, PIL) — FastAPI runs sync routes in a threadpool automatically so they don't block the event loop • Never call a sync blocking function from async def — it will freeze your entire server Background Tasks run after the response is sent to the client: • Client gets 200 immediately • Task runs after (send email, write log, trigger processing) • Uses FastAPI's BackgroundTasks, not asyncio.create_task • For heavy jobs (video encoding, bulk email), use Celery + Redis instead asyncio.gather runs multiple coroutines concurrently — perfect for fetching from multiple APIs in parallel.
ARCHITECTURE
SYNC (blocking):
Request 1 → [DB query 100ms] → Response thread blocked
Request 2 → [waiting...] stuck in queue
ASYNC (non-blocking):
Request 1 → await DB query ─────────────→ Response
Request 2 → [runs during R1 DB wait] → Response
Both complete in ~100ms instead of ~200ms
BACKGROUND TASK FLOW:
POST /send-email
↓
Response: 202 Accepted ← client gets this immediately
↓ (after response sent)
send_email_task() runs in background
(logs, emails, webhooks — all fire-and-forget)
CONCURRENT FETCHING:
Sequential: fetch A (1s) → fetch B (1s) = 2s total
Concurrent: fetch A + fetch B together = 1s total
via asyncio.gather()CODE