Browse Source

📝 Fix StreamingResponse async generator example to actually stream

Add `await asyncio.sleep(0)` to the async generator in the
StreamingResponse documentation example so it properly yields
control back to the event loop between iterations, enabling
true incremental streaming and proper task cancellation.

Without an await point the async generator holds the event loop
for the entire iteration, buffering the full response before
sending it to the client.

Also add a note admonition explaining why the await is needed
and suggesting sync generators as an alternative when there are
no natural await points.

Closes #14680
pull/14993/head
Vishnu Vardhan P 5 months ago
parent
commit
355f2cf01b
  1. 14
      docs/en/docs/advanced/custom-response.md
  2. 3
      docs_src/custom_response/tutorial007_py310.py

14
docs/en/docs/advanced/custom-response.md

@ -167,7 +167,19 @@ You can also use the `status_code` parameter combined with the `response_class`
Takes an async generator or a normal generator/iterator and streams the response body.
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
{* ../../docs_src/custom_response/tutorial007_py310.py hl[1,4,11,17] *}
/// note
Notice the `await asyncio.sleep(0)` inside the async generator function.
In asyncio, a running task only yields control back to the event loop when it hits an `await`. Without any `await` expression, the generator would hold the event loop for the entire iteration, preventing the response from being streamed chunk by chunk and also preventing proper task cancellation.
Adding `await asyncio.sleep(0)` is a lightweight way to give the event loop a chance to send each chunk to the client and handle cancellation between iterations.
If your generator doesn't have any natural `await` points, consider using a **normal (synchronous) generator** with `def` instead to avoid this issue.
///
#### Using `StreamingResponse` with file-like objects { #using-streamingresponse-with-file-like-objects }

3
docs_src/custom_response/tutorial007_py310.py

@ -1,3 +1,5 @@
import asyncio
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
@ -6,6 +8,7 @@ app = FastAPI()
async def fake_video_streamer():
for i in range(10):
await asyncio.sleep(0)
yield b"some fake video bytes"

Loading…
Cancel
Save