Search code examples
pythonpython-3.xfunctionparametersasync-await

When define python async function, what happens and how it works?


Look the following code:

import asyncio

async def count():
    print("One")
    await asyncio.sleep(1)
    print("Two")

async def main():
    await asyncio.gather(count(), count(), count())  # THIS LINE

if __name__ == "__main__":
    import time
    s = time.perf_counter()
    asyncio.run(main())
    elapsed = time.perf_counter() - s
    print(f"{__file__} executed in {elapsed:0.2f} seconds.")

Look at # THIS LINE, asyncio.gather can do its functionality before its parameter count() return value.

But as my understanding toward python. The python interpreter consider the outer function to be a black box and focus on evaluate its parameter first. When all value of its parameter done, the interpreter then pass it to the function to execute.

According to my understanding above, there will be no difference between:

await asyncio.gather(count(), count(), count())

and

await (count(), count(), count())

The later one is a non-assigned tuple.

But how asyncio.gather implement its jobs in such a form?

Or there is something special for async function definition itself?


Solution

  • Because the function count is defined as async def count():, when calling it - instead of executing the function, it returns a coroutine object.

    A coroutine (such as count) starts executing only when using await on it. The way the expression await count() is evaluated is as following:

    1. count is called, returning a coroutine.
    2. await starts waiting on the coroutine.
    3. the coroutine executes, returning None (because the function count returns nothing)
    4. the expression await count() returns None.

    So, to your example - when executing await asyncio.gather(count(), count(), count()):

    1. count is called three times, returning 3 different coroutines.
    2. These coroutines are passed as parameters to asyncio.gather.
    3. asyncio.gather itself returns a coroutine.
    4. await is waiting on asyncio.gather, which awaits on all its parameter coroutines.

    The second expression, await (count(), count(), count()) won't work, as you can't use await on something which is not a coroutine.