Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asyncio - await coroutine more than once (periodic tasks)

I am trying to create a periodic task for an asyncio event loop as shown below, however I am getting a "RuntimeError: cannot reuse already awaited coroutine" exception. Apparently, asyncio does not allow for the same awaitable function to be awaited as discussed in this bug thread. This is how I tried to implement it:

import asyncio    

class AsyncEventLoop:    

    def __init__(self):
        self._loop = asyncio.get_event_loop()

    def add_periodic_task(self, async_func, interval):
        async def wrapper(_async_func, _interval):
            while True:
                await _async_func               # This is where it goes wrong
                await asyncio.sleep(_interval)
        self._loop.create_task(wrapper(async_func, interval))
        return

    def start(self):
        self._loop.run_forever()
        return

Because of my while loop, the same awaitable function (_async_func) would be executed with a sleep interval in between. I got my inspiration for the implementation of periodic tasks from How can I periodically execute a function with asyncio? .

From the bug thread mentioned above, I infer that the idea behind the RuntimeError was so that developers wouldn't accidentally await the same coroutine twice or more, as the coroutine would be marked as done and yield None instead of the result. Is there a way I can await the same function more than once?

like image 870
Arnaud H Avatar asked Jun 30 '18 16:06

Arnaud H


People also ask

What does await do in Asyncio?

The keyword await passes function control back to the event loop. (It suspends the execution of the surrounding coroutine.) If Python encounters an await f() expression in the scope of g() , this is how await tells the event loop, “Suspend execution of g() until whatever I'm waiting on—the result of f() —is returned.

Is coroutine deprecated?

"@coroutine" decorator is deprecated since Python 3.8, use "async def" instead.

How does the Asyncio event loop work?

The event loop is the core of every asyncio application. Event loops run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses. Application developers should typically use the high-level asyncio functions, such as asyncio.

Which is used to write concurrently in Asyncio?

asyncio is a library to write concurrent code using the async/await syntax. asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.


1 Answers

It seems you are confusing async functions (coroutine functions) with coroutines - values that these async functions produce.

Consider this async function:

async def sample():
    await asyncio.sleep(3.14)

You are passing result of its call: add_periodic_task(sample(), 5).

Instead, you should pass async function object itself: add_periodic_task(sample, 5), and call it within your wrapper:

while True:
    await _async_func()
    await asyncio.sleep(_interval)
like image 129
Andrii Maletskyi Avatar answered Sep 26 '22 15:09

Andrii Maletskyi