简体   繁体   中英

Call an async function periodically on a Tornado web server?

I have a web site using Tornado. And I need to call a function ( do_work() in the following exmaple) periodically.

import asyncio
import datetime
from typing import Any, Callable, Coroutine

import tornado
import tornado.ioloop


def schedule_next_sync(loop, func, seconds):
    time = datetime.datetime.timestamp(
        datetime.datetime.now() + datetime.timedelta(seconds=seconds)
    )

    def wrapper():
        loop.run_until_complete(func())
        schedule_next_sync(loop, func, seconds)

    print("The next run is sheduled for %s", datetime.datetime.fromtimestamp(time))
    tornado.ioloop.IOLoop.current().add_timeout(time, wrapper)


async def do_work(x):
    """To be run periodically"""
    print(f"...{datetime.datetime.now()}: {x}")
    await asyncio.sleep(1)
    print('....')


event_loop = asyncio.get_event_loop()
_func = lambda: do_work("test")
schedule_next_sync(event_loop, _func, 3)  # every 3 seconds
tornado.ioloop.IOLoop.current().start()

However, the code got the following error?

The next run is sheduled for %s 2022-03-30 01:48:23.141079
ERROR:tornado.application:Exception in callback functools.partial(<function schedule_next_sync.<locals>.wrapper at 0x000001A7D6B99700>)
Traceback (most recent call last):
  File "C:\users\xxx\anaconda3\envs\x\lib\site-packages\tornado\ioloop.py", line 741, in _run_callback
    ret = callback()
  File ".\test.py", line 23, in wrapper
    loop.run_until_complete(func())
  File "C:\users\xxx\anaconda3\envs\x\lib\asyncio\base_events.py", line 592, in run_until_complete
    self._check_running()
  File "C:\users\xxx\anaconda3\envs\x\lib\asyncio\base_events.py", line 552, in _check_running
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
C:\users\xxx\anaconda3\envs\x\lib\site-packages\tornado\ioloop.py:761: RuntimeWarning: coroutine 'do_work' was never awaited
  app_log.error("Exception in callback %r", callback, exc_info=True)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

You cannot use loop.run_until_complete() when the event loop is already running.

To schedule the execution of async code from sync code or to run async code concurrently, you need to use asyncio.create_task() .

Your example works for me, if I change the wrapper to this:

def wrapper():
        asyncio.create_task(func())
        schedule_next_sync(loop, func, seconds)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM