The behavior of async functions is different even though they are almost the same code, but I don't understand why.
My code is:
import asyncio
async def func1():
await asyncio.sleep(1)
print("func1")
async def func2():
await asyncio.sleep(time)
print("func2")
async def func3():
await asyncio.sleep(3)
print("func3")
async def main():
asyncio.create_task(func1())
await func2()
await func3()
asyncio.run(main())
If I put time = 1
then the output is:
func2
func1
func3
But if time = 2
then the output is:
func1
func2
func3
In the first case, why is func2
printed before func1
even though func2 starts late?
No matter how you get time
into func2
, the difference is clearly between how you start func1
and func2
:
async def main():
asyncio.create_task(func1())
await func2()
...
You create the task, and then instantly await func2
. Then, when func2
goes to sleep, func1
has a chance to start, and also goes to sleep.
If func2
finds that time == 1
, it wakes up after one second and prints, and then after that func1
wakes up, just a fraction later.
If func2
finds that time == 2
, it only wakes up after two seconds, and func1
will have woken up before it.
Your question was "In the first case, why is func2 printed before func1 even though func2 starts late?" - you assume func2
"starts late", but it doesn't. It is awaited before the task created with func1
. You never explicitly await the task, and your program could complete without the task ever completing, but since you have func3
there, waiting substantially longer, you're almost guaranteed it will.
You would have found the order doesn't change when you do this:
async def main():
await func1()
await func2()
...
Because then the two asynchronous functions are awaited in order, and will complete in order when provided with the same sleep()
time.
Similarly, with:
async def main():
t = asyncio.create_task(func1())
await t
await func2()
...
The order is as you expect it, since the task is explicitly awaited before func2()
is started and awaited.
Or of course, just:
async def main():
await asyncio.create_task(func1())
await func2()
...
A good editor or IDE will warn you, if you don't await create_task
. PyCharm warns: "Co-routine create_task is not awaited".
The documentation has this to say: "Important: Save a reference to the result of this function, to avoid a task disappearing mid-execution. The event loop only keeps weak references to tasks. A task that isn’t referenced elsewhere may get garbage collected at any time, even before it’s done. For reliable “fire-and-forget” background tasks, gather them in a collection."