The Complete Guide to FastAPI Asynchronous Programming
📂 Stage: Stage 2 - Advanced Black Technology (Core) 🔗 Related chapters: FastAPIdependency-injection · FastAPImiddleware-application
Table of contents
What is asynchronous programming?
Synchronous vs Asynchronous: The metaphor of waiting in line to eat 🍚
Imagine you go to the cafeteria to get food:
- Synchronized: You stand at the window and wait for the chef to finish frying a dish, take it away, and then order the next dish. You can only wait while the chef cooks.
- Asynchronous: You hand the menu to the chef, then get the cutlery and pour the drinks. When the food is ready, the waiter brings it directly to your table. You don't need to stand around waiting.
This is the core difference: whether you can switch to doing other things while waiting.
Why do web services need to be asynchronous?
Suppose three requests arrive at the same time:
- Synchronous mode: Processed in sequence, total time taken = 200 + 1 + 1000 = 1201ms
- Asynchronous mode: A switches to B while waiting for the database, B completes the switch to C, and switches back to A while C waits → The total time is about 1000ms
In I/O-intensive scenarios (database queries, HTTP requests, file reading and writing), asynchronous allows a single thread to handle a large number of concurrent requests, significantly improving API performance.
Three major advantages of asynchronous programming
- High concurrent processing capability: A single process can handle thousands of concurrent connections
- High resource utilization: avoid the overhead of thread creation and switching
- Fast response: Other requests can be processed while I/O is waiting.
async/await detailed explanation
Basic syntax
await What are you waiting for?
awaitYou can only wait for awaitable objects (Awaitable), including:
asyncio.sleep vs time.sleep
⚠️ is used in FastAPI
time.sleep()Will block the entire event loop!
Event loop principle
What is an event loop?
The event loop is an asynchronous "dispatch center", workflow:
Event loop life cycle demonstration
Asynchronous function calling rules
Four Golden Rules
Calling time-consuming synchronous functions asynchronously
**Why not directlyawaitSynchronous function? **
Because the synchronized function is not a waitable object, it blocks the entire thread. Must useasyncio.to_thread()Throw it to a separate thread pool for execution so that the event loop can continue processing other tasks.
Asynchronous usage in FastAPI
Asynchronous routing vs synchronous routing
FastAPI will automatically detect the routing type:
- if defined as
async def, will run in the event loop and encounterawaitJust hang - if defined as
def, will be thrown into the thread pool for execution, without blocking the main loop (but adding a small amount of thread overhead)
Asynchronous HTTP request (take httpx as an example)
💡 use
httpx.AsyncClientAs a context manager, you can reuse connections instead of creating them temporarily each timeAsyncClientMore efficient.
When to use synchronous code
Applicable scenarios for synchronization code
Rule of Thumb:
- Tasks are mainly I/O (network, disk, database) ➔ Use
async def - Tasks are mainly computational (CPU intensive) ➔ Use
def, FastAPI automatically loses the thread pool - If you are not sure, you can write it first
def, and then optimize after performance testing.
Common Traps and Pit Avoidance Guide
Trap 1: Forget await
Trap 2: Serial await in a loop
Trap 3: Mixing synchronous blocking calls in asynchronous functions
Fix Method:
- Replace with
await asyncio.sleep(2) - or put
time.sleepput inawait asyncio.to_thread(time.sleep, 2)
Trap 4: Misuse of global variables
Since the event loop is single-threaded, no locks are required to modify variables, but the execution order between asynchronous tasks is uncertain, and excessive reliance on shared state is prone to bugs. It is recommended to use local state per request.
Practical Combat: Building an Asynchronous API Service
The following demonstrates a practical example: concurrently obtaining local "database" data and external GitHub API data.
Operating effect: Two independent I/O operations are performed simultaneously, and the total time taken depends on the slowest one, not the sum of the two operations.
Performance optimization suggestions
- Use connection pool
like
httpx.AsyncClient、asyncpg.create_pool, to avoid creating a new connection for each request. - Proper caching
Synchronous calculations are available
functools.lru_cache, asynchronous caching optionalasyncio.LifoQueueOr third-party library. - Avoid unnecessary serial await
Unified use for independent tasks
asyncio.gatherConcurrent execution. - Monitor event loop delays
Production environment can be accessed
asynciodebug mode or use a tool likeprometheus_clientObserve the blockage.
Summary
💡 Remember: The core of asynchronous is to efficiently handle a large number of concurrent I/O requests, but do not do CPU-intensive calculations in asynchronous functions - that will make the entire event loop stuck. Only by rationally using the thread pool and allowing synchronization and asynchronous execution to perform their respective duties can the performance of FastAPI be maximized.

