History
- generators found in Python 3.3
- event loop in the form of
asyncio
: In Python 3.4, theasyncio.coroutine
decorator was used to label a function as acting as a coroutine that was meant for use withasyncio
and its event loop. yield from
toawait
in Python 3.5
Event loop and task
Asynchronous Generators
A Python generator is any function containing one or more yield expressions:
def func(): # a function
return
def genfunc(): # a generator function
yield
We propose to use the same approach to define asynchronous generators:
async def coro(): # a coroutine function
await smth()
async def asyncgen(): # an asynchronous generator function
await smth()
yield 42
The result of calling an asynchronous generator function
is an asynchronous generator object
, which implements the asynchronous iteration protocol defined in PEP 492.
It is a SyntaxError
to have a non-empty return
statement in an asynchronous generator.
Concepts
- Generations:
yield
,yield from
- Awaitable: used for await expression.
- Future-like object: An object with an
__await__
method - Coroutine object
- Generator-based coroutines (function):
@types.coroutine
+yield
- Native coroutines (function):
async def
+await
+return value
- Generator-based coroutines (function):
- Future-like object: An object with an
- Asynchronous context manager: An asynchronous context manager has
__aenter__
and__aexit__
methods and can be used withasync with
. - Asynchronous iterable: An object with an
__aiter__
method, which must return an asynchronous iterator object. Can be used withasync for
.- Asynchronous iterator: An asynchronous iterator has an
__anext__
method
- Asynchronous iterator: An asynchronous iterator has an
Key properties of coroutines:
async def
functions are always coroutines, even if they do not containawait
expressions.- It is a
SyntaxError
to haveyield
oryield from
expressions in an async function.
Await expression
async def read_data(db):
data = await db.fetch('SELECT ...')
...
await
, similarly to yield from
, suspends execution of read_data coroutine until db.fetch
awaitable
completes and returns the result data.
It uses the yield from
implementation with an extra step of validating its argument.
await
only accepts an awaitable, which can be one of:
- A native coroutine object returned from a native coroutine function.
- A generator-based coroutine object returned from a function decorated with
types.coroutine()
. - An object with an
__await__
method returning aniterator
.- Any
yield from
chain of calls ends with ayield
. This is a fundamental mechanism of howFutures
are implemented. Since, internally, coroutines are a special kind of generators, everyawait
is suspended by ayield
somewhere down the chain ofawait
calls - To enable this behavior for coroutines, a new magic method called
__await__
is added. Inasyncio
, for instance, to enableFuture
objects in await statements, the only change is to add__await__ = __iter__
line toasyncio.Future
class. - Objects with
__await__
method are calledFuture-like
objects in the rest of this PEP. - It is a
TypeError
if__await__
returns anything but an iterator.
- Any
Exceptions
- It is a
SyntaxError
to useawait
outside of anasync def
function (like it is aSyntaxError
to useyield
outside ofdef
function). - It is a
TypeError
to pass anything other than anawaitable object
to anawait
expression.
Asynchronous context manager
An asynchronous context manager is a context manager that is able to suspend execution in its enter and exit methods.
To make this possible, a new protocol for asynchronous context managers is proposed. Two new magic methods are added: __aenter__
and __aexit__
. Both must return an awaitable.
Call asynchronous context manager using async with
.
Asynchronous iterators
An asynchronous iterable
is able to call asynchronous code in its iter
implementation, and asynchronous iterator
can call asynchronous code in its next
method. To support asynchronous iteration:
- An object must implement an
__aiter__
method returning an asynchronous iterator object. - An asynchronous iterator object must implement an
__anext__
method returning an awaitable. - To stop iteration
__anext__
must raise aStopAsyncIteration
exception.
async for TARGET in ITER:
BLOCK
else:
BLOCK2