Create Task

asyncio.create_task API is used to schedule the execution of a coroutine. It puts the coroutine in the event loop and if there is nothing else to execute, event loop executes that coroutine.

create_task method takes a coroutine and returns a task that needs to be awaited for the execution.

Below example shows use of create_task API, followed by a proper explanation of the intended behavior.

import asyncio
from time import sleep, time 
 
def with_time(fn):
    async def wrapper():
        start = time()
        await fn()
        print("total time elapsed: ", time() - start)
 
    return wrapper
 
 
async def timeout(n):
    print("before timeout", n)
    await asyncio.sleep(5)
    print("after timeout", n)
 
 
async def async_print():
    print("Hello world")
 
 
@with_time
async def main():
    task = asyncio.create_task(timeout(1))
    task2 = asyncio.create_task(async_print())
    await timeout(2)
    await task
    await task2
 
 
if __name__ == "__main__":
    asyncio.run(main())

It gives the output as follows,

before timeout 2
before timeout 1
Hello world
after timeout 2
after timeout 1
total time elapsed:  5.00157618522644

Explanation

  1. create_task schedules timeout and async_print coroutines.
  2. timeout(2) coroutine is started where there is a asyncio.sleep. This releases the control.
  3. There is timeout(1) already scheduled, so event loop picks it up and timeout 1 log comes.
  4. timeout(1) has a asyncio.sleep which releases the control.
  5. There is async_print already scheduled, so event loop picks it up and we see the log.
  6. In the end, those tasks are awaited which holds the program until timeout logs come and then total time elapsed log comes.

Note

If we do not await the tasks, the total elapsed time log comes before the rest of timeout logs because the control is not holded.