FastAPI 支持 async def 和常规 def 两种 path operations。这个选择对性能很重要:当你可以 await 非阻塞 I/O 时使用 async def,当你的代码调用阻塞(同步)操作时使用常规 def。
@app.get("/async")
async def async_endpoint():
data = await fetch_from_api() # await non-blocking I/O — efficient
return data
@app.get("/sync")
def sync_endpoint():
data = blocking_db_call() # ordinary blocking code
return data
async def → runs on the main event loop. Efficient ONLY if you await non-blocking calls.
⚠️ A BLOCKING call inside async def blocks the WHOLE event loop → kills concurrency!
def → FastAPI runs it in a THREAD POOL, so blocking code doesn't block the event loop.
Safe for synchronous/blocking libraries.
# ✅ async def — when you can await async libraries (httpx, async DB drivers)
async def get_user():
async with httpx.AsyncClient() as c:
return await c.get(url)
# ✅ plain def — when using SYNCHRONOUS/blocking libraries (requests, sync ORM)
def get_user():
return requests.get(url).json() # blocking → FastAPI runs it in a thread
# ❌ THE DANGEROUS MISTAKE — blocking call inside async def
async def bad():
return requests.get(url).json() # blocks the event loop! Use `def` or an async client
关键规则:永远不要在 async def 中放置阻塞调用 — 它会阻塞事件循环并破坏并发。要么使用带有 await 的异步库,要么使用普通 def(FastAPI 会安全地在线程池中运行它)。
在 async def 和 def 之间正确选择直接影响你的 API 的性能和并发,选择错误是一个常见的、严重的错误。
关键的见解是 async def 只有在你 await 非阻塞操作时才有益 — 把阻塞调用(比如同步的 requests 库或阻塞的数据库驱动)放入 async def 会阻塞整个事件循环,冻结所有并发请求,破坏异步的目的。
相反,FastAPI 巧妙地在线程池中运行普通 def 端点,所以同步/阻塞代码在那里是安全的。
理解这条规则 — 对真正的异步库使用 async def,对阻塞代码使用普通 def,永远不要在 async def 中混入阻塞调用 — 对编写高性能的 FastAPI 端点和避免一个微妙但毁灭性的错误(即意外地将所有请求序列化到被阻塞的事件循环后面)至关重要。