Reputation: 26588
Complete newb here, reading about Asycnio Tasks which has this example:
import asyncio
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
task1 = asyncio.create_task(
say_after(1, 'hello'))
task2 = asyncio.create_task(
say_after(2, 'world'))
print(f"started at {time.strftime('%X')}")
# Wait until both tasks are completed (should take
# around 2 seconds.)
await task1
await task2
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
My original understanding of await
is that it blocks the execution of current function and wait for the async function to return.
But in this case both coroutines are executed concurrently, it doesn't fit well my understanding of await
. Could someone please explain?
Further investigation, by adding extra print
in say_after
, it seems to me the coroutine doesn't start until await
happens...
import asyncio
import time
async def say_after(delay, what):
print('Received {}'.format(what))
await asyncio.sleep(delay)
print(what)
async def main():
task1 = asyncio.create_task(
say_after(1, 'hello'))
task2 = asyncio.create_task(
say_after(2, 'world'))
print(f"started at {time.strftime('%X')}")
# Wait until both tasks are completed (should take
# around 2 seconds.)
await task1
await task2
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
prints
started at 13:41:23
Received hello
Received world
hello
world
finished at 13:41:25
Upvotes: 2
Views: 2017
Reputation: 46
Your understanding of await is correct. It does pause the execution of the main function.
The key is that asyncio.create_task()
creates a task and schedules it.
So the say_after
function starts running here:
task1 = asyncio.create_task(
say_after(1, 'hello'))
task2 = asyncio.create_task(
say_after(2, 'world'))
and not when you're await
ing.
See the asyncio.create_task
documentation
Upvotes: 2
Reputation: 26588
OK Both @tsuyoku and @Fanto answers are correct, this answer is just to compliment the existing answers, the important bit for me that I could not grasp was the execution started on create_task()
:
import asyncio
import time
async def say_after(delay, what):
print('Received {}'.format(what))
await asyncio.sleep(delay)
print(what)
async def main():
task1 = asyncio.create_task(
say_after(1, 'hello')
)
task2 = asyncio.create_task(
say_after(2, 'world')
)
print(f"started at {time.strftime('%X')}")
await asyncio.sleep(10)
# Wait until both tasks are completed (should take
# around 2 seconds.)
await task2
print('task 2 finished')
await task1
print('task 1 finished')
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
prints
started at 10:42:10
Received hello
Received world
hello
world
task 2 finished
task 1 finished
finished at 10:42:20
the original misunderstanding is that stuff takes a bit of time to run the tasks, and the original print out in my question misguided me to think the task doesn't run until the await
statement.
Upvotes: 1
Reputation: 148
When you encapsulate a coroutine in a Task (or Future) object the coroutine is ready to work, so when the event loop start running on the first await, both task1 and task2 are running.
Trying to make it clearer, to execute a coroutine you need two things:
1) a coroutine incapsulated in a future object(Task) to make it awaitable
2) a running event loop
In your example the execution works like this:
1 - create_task1
2 - create_task2
3 - await task1
4 - await sleep of task1
5 - await sleep of task2
now both task1 and task2 are sleeping so, suppose task1 is the first to finish (sleep some time)
6 - print of task1
7 - await task2
8 - print of task2
now the loop end
As you said when you got an await the execution stops, but let me say that stops just in the current "execution flow", when you create a future(Task) you create another exucution flow, and the await just switch to the current execution flow. This last explanation is not completely correct in sens of terms but help to make it clearer.
I hope i was clear. P.S.: sorry for my bad english.
Upvotes: 1