Search code examples
pythonyieldpython-3.5python-asyncio

How to use 'yield' inside async function?


I want to use generator yield and async functions. I read this topic, and wrote next code:

import asyncio

async def createGenerator():
    mylist = range(3)
    for i in mylist:
        await asyncio.sleep(1)
        yield i*i

async def start():
    mygenerator = await createGenerator()
    for i in mygenerator:
        print(i)

loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(start())

except KeyboardInterrupt:
    loop.stop()
    pass

But i got the error:

SyntaxError: 'yield' inside async function

How to use yield generator in async function?


Solution

  • Upd:

    Starting with Python 3.6 we have asynchronous generators and able to use yield directly inside coroutines.

    import asyncio
    
    
    async def async_generator():
        for i in range(3):
            await asyncio.sleep(1)
            yield i*i
    
    
    async def main():
        async for i in async_generator():
            print(i)
    
    
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    finally:
        loop.run_until_complete(loop.shutdown_asyncgens())  # see: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.shutdown_asyncgens
        loop.close()
    

    Old answer for Python 3.5:

    You can't yield inside coroutines. Only way is to implement Asynchronous Iterator manually using __aiter__/__anext__ magic methods. In your case:

    import asyncio
    
    
    class async_generator:
        def __init__(self, stop):
            self.i = 0
            self.stop = stop
    
        async def __aiter__(self):
            return self
    
        async def __anext__(self):
            i = self.i
            self.i += 1
            if self.i <= self.stop:
                await asyncio.sleep(1)
                return i * i
            else:
                raise StopAsyncIteration
    
    
    async def main():
        async for i in async_generator(3):
            print(i)
    
    
    if __name__ == "__main__":
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
    

    Output:

    0
    1
    4
    

    Here're two more examples: 1, 2