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?
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:
await
starts waiting on the coroutine.None
(because the function count
returns nothing)await count()
returns None
.So, to your example - when executing await asyncio.gather(count(), count(), count())
:
count
is called three times, returning 3 different coroutines.asyncio.gather
.asyncio.gather
itself returns a coroutine.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.