I\'m trying to wrap my head around async/await in python.
Am I on the right track?
async
and @coroutine
functions returns corouti
async
and@coroutine
functions returns coroutine/generator, not the returned value
To be technical, types.coroutine
returns a generator-based coroutine which is different than generators and different than coroutines.
await extracts the actual return value of coroutine/generator.
await, similar to yield from
, suspends the execution of the coroutine until the awaitable it takes completes and returns the result.
async function result (coroutines) is meant to be added to event-loop.
Yes.
await creates "bridge" between event-loop and awaited coroutine (enabling the next point).
await creates a suspension point that indicates to the event loop that some I/O operation will take place thereby allowing it to switch to another task.
@coroutine's yield communicates directly with event-loop. (skipping direct caller which awaits the result)
No, generator-based coroutines use yield from
in a similar fashion to await
, not yield
.
await can be used only inside async functions.
Yes.
yield can be used only inside coroutine.
yield from
can be used inside generator-based coroutines (generators decorated with types.coroutine
) and, since Python 3.6, in async
functions that result in an asynchronous generator.
Demo code:
(illustrates whole control flow between async
and types.coroutine
and event loop)
import types
class EL:
"""Fake An event loop."""
def __init__(self, outer_async):
self.outer_async = outer_async
def loop(self):
print(' EL.loop : outer_async.send(None)')
send_result = self.outer_async.send(None) # seed outer_async.
print(' EL.loop : outer_async.send(None) -> outer_async_send_result = {}'.format(send_result))
do_loop = True
loop_counter = 0
while do_loop:
print()
loop_counter += 1
try:
arg = send_result + '-loop(send-{})'.format(loop_counter)
print(' EL.loop.while : task.outer_async.send({})'.format(arg))
send_result = self.outer_async.send(arg) # raises StopIteration.
print(' EL.loop.while : task.outer_async.send({}) -> send_result = {}'.format(arg, send_result))
except StopIteration as e:
print(' EL.loop.while : except StopIteration -> {}'.format(e.value))
do_loop = False
return loop_counter
async def outer_async(label):
inner_coro_arg = label + '-A1'
print(' outer_async({}) : await inner_coro({})'.format(label, inner_coro_arg))
await_result = await inner_coro(inner_coro_arg)
print(' outer_async({}) : await inner_coro({}) -> await_result = {}'.format(label, inner_coro_arg, await_result))
inner_coro_arg = label + '-A2'
print(' outer_async({}) : await inner_coro({})'.format(label, inner_coro_arg))
await_result = await inner_coro(inner_coro_arg)
print(' outer_async({}) : await inner_coro({}) -> await_result = {}'.format(label, inner_coro_arg, await_result))
return 555555
@types.coroutine
def inner_coro(inner_coro_label):
yld_arg = inner_coro_label + '-C(yield)'
print(' inner_coro({}) : yield({})'.format(inner_coro_label, yld_arg))
yield_result = yield yld_arg
print(' inner_coro({}) : yield({}) -> yield_result = {}'.format(inner_coro_label, yld_arg, yield_result))
return_value = yield_result + '-C(return)'
print(' inner_coro({}) : return -> {}'.format(inner_coro_label, return_value))
return return_value
def main():
loop = EL(outer_async('$$'))
print('main() : loop.loop')
loop_outer_async = loop.loop()
print('main() : loop.loop -> {}'.format(loop_outer_async))
if __name__ == '__main__':
main()
Result:
main() : loop.loop
EL.loop : outer_async.send(None)
outer_async($$) : await inner_coro($$-A1)
inner_coro($$-A1) : yield($$-A1-C(yield))
EL.loop : outer_async.send(None) -> outer_async_send_result = $$-A1-C(yield)
EL.loop.while : task.outer_async.send($$-A1-C(yield)-loop(send-1))
inner_coro($$-A1) : yield($$-A1-C(yield)) -> yield_result = $$-A1-C(yield)-loop(send-1)
inner_coro($$-A1) : return -> $$-A1-C(yield)-loop(send-1)-C(return)
outer_async($$) : await inner_coro($$-A1) -> await_result = $$-A1-C(yield)-loop(send-1)-C(return)
outer_async($$) : await inner_coro($$-A2)
inner_coro($$-A2) : yield($$-A2-C(yield))
EL.loop.while : task.outer_async.send($$-A1-C(yield)-loop(send-1)) -> send_result = $$-A2-C(yield)
EL.loop.while : task.outer_async.send($$-A2-C(yield)-loop(send-2))
inner_coro($$-A2) : yield($$-A2-C(yield)) -> yield_result = $$-A2-C(yield)-loop(send-2)
inner_coro($$-A2) : return -> $$-A2-C(yield)-loop(send-2)-C(return)
outer_async($$) : await inner_coro($$-A2) -> await_result = $$-A2-C(yield)-loop(send-2)-C(return)
EL.loop.while : except StopIteration -> 555555
main() : loop.loop -> 2