Async/Await¶
Similar to the yield
statement and generators, every function with an async
prefix is a coroutine function. Calling the coroutine function doesn’t execute
the code directly instead it returns a coroutine object.
async def foo():
pass
print(type(foo))
coro = foo()
print(type(coro))
print(dir(coro))
Output:
>>> print(type(foo))
<class 'function'>
>>> coro = foo()
>>> print(type(coro))
<class 'coroutine'>
>>> print(dir(coro))
['__await__', ...]
>>> "__await__" in dir(f)
True
>>> "__iter__" in dir(f)
False
These coroutine objects implement the Awaitable
protocol but the aren’t
Iterables
.
class Coroutine(Awaitable):
def send(self, value):
"""Send a value into the coroutine.
Return next yielded value or raise StopIteration.
"""
def throw(self, typ, val=None, tb=None):
"""Raise an exception in the coroutine.
Return next yielded value or raise StopIteration.
"""
def close(self):
"""Raise GeneratorExit inside coroutine.
"""
try:
self.throw(GeneratorExit)
except (GeneratorExit, StopIteration):
pass
else:
raise RuntimeError("coroutine ignored GeneratorExit")
Besides the async def
function definition there are
additional new expressions and statements. await
for awaiting a coroutine result, async for
for iterating over async iterables and async with
for async context managers.
await
expressions,async for
andasync with
can only be used in the body of a coroutine function.Functions defined with async def syntax are always coroutine functions, even if they do not contain await or async keywords.
Summary
Coroutines
are now explicit objects and notGenerators
anymore despite sharing a very very similar interface.Coroutines
areAwaitables
but also notIterables
.If
await
is used in a function it must be declared as async and therefore becomes a native coroutine function.