Python asynchronous generators

-2

With the introduction of the asyncio library, a new syntax has been introduced to define corotines in Python> = 3.5 and with it you can define asynchronous methods, asynchronous iterators, and even asynchronous generators.

How can I implement asynchronous generators and what would be their common use cases?

    
asked by anonymous 20.06.2018 / 19:33

1 answer

2

The best source of information about asynchronous generators is the PEP that describes their need, and their implementation - the PEP 525 .

To briefly describe asynchronous generators, let's briefly recap the use of asynchronous functions (but not their internal mechanism). Asynchronous code in Python and other languages is characterized by code capable of running on a single thread, but in which the programmer can put explicit points where the code of a function (generator, method, etc.) ..) can be paused while waiting for a result. During this pause, a "controller" of the execution of the asynchronous code passes the execution to other asynchronous functions paused in the same way, check if results have been expected, etc ....

In Python this pause is characterized by the keywords await and async - whenever one of them appears in the body of a function, the code ' should be running in an asynchronous context. / p>

It turns out that in Python 3.5 the command async for - that is, a for has already been defined, where at each inertia the program control is passed to the event loop until the next element of the iterator is available. The iterator to be used in a async for in Python 3.5 would have to define the special methods __aiter__ and __anext__ as co-routines (asynchronous functions, declared with async def ) - in contrast to __iter__ and __next__ of normal iterators.

But for a normal%, non-asynchronous%, any function that contains the expression for in your body is automatically converted to yield : that is, an object that has methods generator and __iter__ and can be used in __next__ . A for , before Python 3.6 and PEP 525, could only iterate over a class defined with the asynchronous versions of these methods, and attempting to use the expression async for in the body of an asynchronous function resulted in a syntax error :

Python 3.5.5 (default, Jun  8 2018, 09:55:12) 
...
>>> async def a():
...    yield 1
... 
  File "<stdin>", line 2
SyntaxError: 'yield' inside async function

Only in Python 3.6 can you directly declare an async generator:

Python 3.6.5 (default, Jun  8 2018, 09:56:48) 
...
>>> async def a():
...    yield 1
... 
>>> 

And it is usable, in another asynchronous function, always managed by the event loop, by the command yield :

import asyncio

loop = asyncio.get_event_loop()

import random
async def random_pause_generator(start, stop):
    for i in range(start, stop):
        await asyncio.sleep(random.random())
        yield i

async def counter(start, stop):
    async for j in random_pause_generator(start, stop):
        print(j)


async def main():
    await asyncio.gather(counter(0, 10), counter(110, 120))

loop.run_until_complete(main())

Execute this code and notice how the two calls to the "counter" routine are executed in parallel, with results of each call appearing out of order. It is interesting to note the async for call that unifies the two calls waiting in a "single hold". If you simply use asyncio.gather , the end result is the same as in a synchronous function: the program would only continue on the bottom line after the await counter(10, 20) call resolves itself completely.

Extra note about async: The controller code in Python is simply referred to by the "event loop", and is a special object that, in case of use of the asyncio library can always be retrieved by the counter call. (Note that it is possible to have other library implementations to coordinate the execution of asynchronous code, and they may have distinct calls to retrieve the event loop - but since asyncio is flexible enough to allow the use of other classes as event loop, this would be just reinvent the wheel)

    
22.06.2018 / 16:01