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.
awaitexpressions,async forandasync withcan 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
Coroutinesare now explicit objects and notGeneratorsanymore despite sharing a very very similar interface.CoroutinesareAwaitablesbut also notIterables.If
awaitis used in a function it must be declared as async and therefore becomes a native coroutine function.