Search code examples
python-asyncio

Create an async generator "from scratch"


Most of the time I am using asyncio API. But how would I create an async generator "from scratch"?

Say I have a classic generator, and I want to make it async. How can I do that?

I naively thought that I could do something like below, but it does not run asynchronously (the 3 "for loops" are running one after the other, instead of concurrently). My hope was to make some_loop() asynchronous by calling it from some_async_loop() and releasing the event loop after each iteration with asyncio.sleep(0):

#!/usr/bin/env python3

import asyncio


async def run():
    task = asyncio.ensure_future(run_async_loop())
    asyncio.ensure_future(run_async_loop())
    asyncio.ensure_future(run_async_loop())

    await task

async def run_async_loop():
    async for i in some_async_loop():
        print(i)

async def some_async_loop():
    for i in some_loop():
        yield i
        asyncio.sleep(0)

def some_loop():
    for i in range(10):
        yield i


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())

Solution

  • I'm glad the fixing the call to asyncio.sleep() got things running.

    I'm concerned partly about your run() function, because you're only await-ing on the first task, which means your code could exit before the other tasks are complete. I would suggest this:

    async def run():
        tasks = [run_async_loop(), run_async_loop(), run_async_loop()]
        await asyncio.gather(*tasks)
    

    I think you can also simplify your __main__ block:

    if __name__ == '__main__':
        asyncio.run(run())