84

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?

6
  • 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. Commented May 31, 2016 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. Commented May 31, 2016 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? Commented May 31, 2016 at 15:56
  • May be use Event? createGenerator will be set event, and start will be wait event. I wrote this solution. It's work, but i want more nice code. Commented May 31, 2016 at 16:03
  • @Ильдар did you see second answer? What you think about it? Looks like it works. Commented Jun 2, 2016 at 12:15

3 Answers 3

118

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

Sign up to request clarification or add additional context in comments.

1 Comment

Based on your Python 3.6 code I made an example for using matlibplot for if anyone is interested: stackoverflow.com/questions/44163601/…
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.

Comments

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 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.