Search code examples
pythonpython-asyncio

Why is "await ..." not the same as "a = ..." followed by "await a"?


The following code illustrates how to use coroutines with asyncio.

The output of consecutive and parallel_fail is:

SLEEP for 2.5 seconds     # 0
back after 2.5 seconds    # 2.5
SLEEP for 2 seconds       # 2.5
back after 2 seconds      # 4.5

(printed after 0, 2.5, 2.5 and 4.5 seconds)

And the output of parallel is:

SLEEP for 2.5 seconds     # 0
SLEEP for 2 seconds       # 0
back after 2 seconds      # 2
back after 2.5 seconds    # 2.5

The outputs of consecutive and parallel are expected.
But why is parallel_fail like consecutive, and not like parallel?

One would expect that await something is equivalent to a = something followed by await a, right?

import asyncio


async def f(n):
    print(f'SLEEP for {n} seconds')
    await asyncio.sleep(n)
    print(f'back after {n} seconds')


async def consecutive():
    print('consecutive:')
    await f(2.5)
    await f(2)


async def parallel():
    print('parallel:')
    a = asyncio.create_task(f(2.5))
    b = asyncio.create_task(f(2))
    await a
    await b


async def parallel_fail():
    print('parallel fail:')
    await asyncio.create_task(f(2.5))
    await asyncio.create_task(f(2))


asyncio.run(consecutive())
print('----------------------')
asyncio.run(parallel())
print('----------------------')
asyncio.run(parallel_fail())

Solution

  • The behaviour you observe in parallel() is as follows:

    • create (schedule) task f(2.5) and assign the task to variable a
    • create (schedule) task f(2) and assign the task to variable b
    • await for task a to finish (this will take 2.5 seconds)
    • meanwhile task b (that is scheduled) finished after 2 seconds
    • await b doesn't await for anything - because task b finished

    So here you see parallel running.


    The behaviour you observe in parallel_fail() is as follows:

    • create (schedule) task f(2.5) and immediately await it (this takes 2.5sec)
    • afterwards, create (schedule) task f(2) and immediately await it (this takes 2sec)

    Here you see normal sequential running.