I'm migrating from tornado
to asyncio
, and I can't find the asyncio
equivalent of tornado
's PeriodicCallback
. (A PeriodicCallback
takes two arguments: the function to run and the number of milliseconds between calls.)
asyncio
?RecursionError
after a while?It should be used as a main entry point for asyncio programs, and should ideally only be called once. New in version 3.7.
The asyncio. sleep() method suspends the execution of a coroutine. Coroutines voluntarily yield CPU leading to co-operative multitasking through the await keyword.
To run an async function (coroutine) you have to call it using an Event Loop. Event Loops: You can think of Event Loop as functions to run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses. Example 1: Event Loop example to run async Function to run a single async function: Python3.
gather() method - It runs awaitable objects (objects which have await keyword) concurrently.
For Python versions below 3.5:
import asyncio @asyncio.coroutine def periodic(): while True: print('periodic') yield from asyncio.sleep(1) def stop(): task.cancel() loop = asyncio.get_event_loop() loop.call_later(5, stop) task = loop.create_task(periodic()) try: loop.run_until_complete(task) except asyncio.CancelledError: pass
For Python 3.5 and above:
import asyncio async def periodic(): while True: print('periodic') await asyncio.sleep(1) def stop(): task.cancel() loop = asyncio.get_event_loop() loop.call_later(5, stop) task = loop.create_task(periodic()) try: loop.run_until_complete(task) except asyncio.CancelledError: pass
When you feel that something should happen "in background" of your asyncio program, asyncio.Task
might be good way to do it. You can read this post to see how to work with tasks.
Here's possible implementation of class that executes some function periodically:
import asyncio from contextlib import suppress class Periodic: def __init__(self, func, time): self.func = func self.time = time self.is_started = False self._task = None async def start(self): if not self.is_started: self.is_started = True # Start task to call func periodically: self._task = asyncio.ensure_future(self._run()) async def stop(self): if self.is_started: self.is_started = False # Stop task and await it stopped: self._task.cancel() with suppress(asyncio.CancelledError): await self._task async def _run(self): while True: await asyncio.sleep(self.time) self.func()
Let's test it:
async def main(): p = Periodic(lambda: print('test'), 1) try: print('Start') await p.start() await asyncio.sleep(3.1) print('Stop') await p.stop() await asyncio.sleep(3.1) print('Start') await p.start() await asyncio.sleep(3.1) finally: await p.stop() # we should stop task finally if __name__ == '__main__': loop = asyncio.get_event_loop() loop.run_until_complete(main())
Output:
Start test test test Stop Start test test test [Finished in 9.5s]
As you see on start
we just start task that calls some functions and sleeps some time in endless loop. On stop
we just cancel that task. Note, that task should be stopped at the moment program finished.
One more important thing that your callback shouldn't take much time to be executed (or it'll freeze your event loop). If you're planning to call some long-running func
, you possibly would need to run it in executor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With