Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I periodically execute a function with asyncio?

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.)

  • Is there such an equivalent in asyncio?
  • If not, what would be the cleanest way to implement this without running the risk of getting a RecursionError after a while?
like image 627
2Cubed Avatar asked May 29 '16 16:05

2Cubed


People also ask

How many times should Asyncio run () be called?

It should be used as a main entry point for asyncio programs, and should ideally only be called once. New in version 3.7.

What is Asyncio sleep ()?

The asyncio. sleep() method suspends the execution of a coroutine. Coroutines voluntarily yield CPU leading to co-operative multitasking through the await keyword.

How do I call a function asynchronously in Python?

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.

Which function is used to run Awaitables concurrently in Asyncio?

gather() method - It runs awaitable objects (objects which have await keyword) concurrently.


2 Answers

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 
like image 114
A. Jesse Jiryu Davis Avatar answered Oct 02 '22 16:10

A. Jesse Jiryu Davis


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.

like image 20
Mikhail Gerasimov Avatar answered Oct 02 '22 15:10

Mikhail Gerasimov