Describe the bug
async BackgroundTasks are not running as background tasks. My response blocks until the task finishes.
To Reproduce
Steps to reproduce the behavior:
run.py with contentsfrom fastapi import FastAPI, BackgroundTasks
import time
import asyncio
app = FastAPI()
async def sleep_for_10_seconds():
start = time.time()
while True:
now = time.time()
if now - start > 10:
break
asyncio.sleep(1)
print(time.time(), 'slept for 10 seconds')
@app.get("/")
async def read_root(background_tasks: BackgroundTasks):
print(time.time(), 'read_root')
background_tasks.add_task(sleep_for_10_seconds)
return {"Hello": "World"}
uvicorn run:app --reloadcurl http://localhost:8000Expected behavior
{"Hello": "World"} returns immediately. Instead it takes 10 seconds.
Environment:
fastapi==0.27.0uvicorn==0.8.4Additional context
I originally thought this problem was confined to asyncio.ensure_future() in #403, but I'm getting the same behavior with fastapi.BackgroundTasks.
sync tasks return immediately (i.e. don't define with async).
try with def sleep_for_10_seconds(): instead of async def sleep_for_10_seconds():
@euri10 That may be a reasonable short term workaround, but I don鈥檛 think you should have to pay the threadpool overhead just to get background tasks to finish after returning the response.. If this is the behavior on the most recent version of FastAPI I think it merits a fix.
@ebanner I鈥檒l look into this as soon as I can (hopefully tomorrow), but in the mean time could you confirm that it isn鈥檛 fixed by updating to the most recent version of fastapi?
/agree
you can also use it the Starlette way but you have to declare your JSONResponse and won't benefit from the docs goodies
import time
import asyncio
app = FastAPI()
async def sleep_for_10_seconds():
start = time.time()
while True:
now = time.time()
if now - start > 10:
break
asyncio.sleep(1)
print(time.time(), 'slept for 10 seconds')
@app.get("/")
async def read_root():
print(time.time(), 'read_root')
background_tasks = BackgroundTasks()
background_tasks.add_task(sleep_for_10_seconds)
return JSONResponse({"Hello": "World"}, background=background_tasks)
@euri10 @ebanner I think I found the problem:
async def sleep_for_10_seconds():
start = time.time()
while True:
now = time.time()
if now - start > 10:
break
await asyncio.sleep(1) # <-- HERE (was missing the await)
print(time.time(), 'slept for 10 seconds')
After making this change, it seems to behave properly.
evil :+1:
Ah, my mistake!
Thank you for pointing it out.
Thanks @dmontagu and @euri10 for the help!
Thanks @ebanner for reporting back and closing the issue.