80

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?

Community
  • 1
  • 1
Ильдар
  • 963
  • 1
  • 7
  • 11
  • 2
    Is that possible? It seems like two opposing designs. Generators are made not to produce value unless needed, it means they in principle need to handle having a *state*. `async` on the other hand would suggest that the called function cannot depend on it state. Otherwise you will have data races. It seems really cumbersome to support async generators, they would need to be wrapped with locking mechanisms. So probably answer to your question is somewhere in this direction. – luk32 May 31 '16 at 15:39
  • can you return a Future object and then yield that object when you want it's data. I've never used asyncio, but that's how it's done with Tornado. – reticentroot May 31 '16 at 15:45
  • I don't think that an asyncrounus generator makes any sense. You should be able to return a generator from an async function. Is there something you want to achieve or are you just trying things out? – syntonym May 31 '16 at 15:56
  • May be use Event? createGenerator will be set event, and start will be wait event. I wrote [this solution](http://pastebin.com/raw/087Ai2Lw). It's work, but i want more nice code. – Ильдар May 31 '16 at 16:03
  • @Ильдар did you see second answer? What you think about it? Looks like it works. – Mikhail Gerasimov Jun 02 '16 at 12:15
  • Possible duplicate of [Lazy iterators (generators) with asyncio](http://stackoverflow.com/questions/37280141/lazy-iterators-generators-with-asyncio) – vad Jun 07 '16 at 06:50

3 Answers3

108

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

Slam
  • 8,112
  • 1
  • 36
  • 44
Mikhail Gerasimov
  • 36,989
  • 16
  • 116
  • 159
  • 1
    Based on your Python 3.6 code I made an example for using `matlibplot` for if anyone is interested: https://stackoverflow.com/questions/44163601/asyncio-matplotlib-show-still-freezes-program/44175558#44175558 – NumesSanguis May 25 '17 at 08:14
8

New Python 3.6 comes with support for asynchronous generators.

PEP 0525

What's new in Python 3.6

PS: On the moment of writing Python 3.6 is still beta. If you are on GNU/Linux or OS X and you cannot wait you can try new Python with pyenv.

rominf
  • 2,719
  • 3
  • 21
  • 39
4

This should work with python 3.6 (tested with 3.6.0b1):

import asyncio

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

async def start():
    async for i in createGenerator():
        print(i)

loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(start())

except KeyboardInterrupt:
    loop.stop()
    pass
Matus
  • 603
  • 4
  • 6