I need to schedule a periodic function call in python (ie. called every minute), without blocking the event loop (I'm using Quart framework with asyncio).
Essentially need to submit work onto the event loop, with a timer, so that the webserver keeps serving incoming requests in the meantime and roughly every minute it calls my function.
I tried many ways, for instance:
def do_work():
print("WORK", flush=True)
async def schedule():
await asyncio.sleep(0)
print("scheduling")
loop = asyncio.get_running_loop()
t = loop.call_later(2, do_work)
print("scheduled")
asyncio.run(schedule())
But it either never gets executed (like the code above), or it blocks the webserver main event loop. For instance, with the code above I would expect (since it's done within asyncio.run
and schedule
awaits timer) that "scheduling" would be printed after (or during) the server setup, but that's not the case, it blocks.
You can use a background task that is started on startup,
async def schedule():
while True:
await asyncio.sleep(1)
await do_work()
@app.before_serving
async def startup():
app.add_background_task(schedule)
which will run schedule
for the lifetime of the app, being cancelled at shutdown.
Quart-Tasks now exists which allows the following,
from quart import Quart
from quart_tasks import QuartTasks
app = Quart(__name__)
tasks = QuartTasks(app)
@tasks.periodic(timedelta(seconds=1))
async def schedule():
... # Do something
It also allows the schedule to be defined using the CRON syntax.