Python asyncio.create_task()

Summary: in this tutorial, you’ll learn how to use asyncio.create_task() function to run multiple tasks concurrently.

Simulating a long-running operation

To simulate a long-running operation, you can use the sleep() coroutine of the asyncio package. The sleep() function delays a specified number of the second:

await asyncio.sleep(seconds)Code language: Python (python)

Because sleep() is a coroutine, you need to use the await keyword. For example, the following uses the sleep() coroutine to simulate an API call:

import asyncio


async def call_api(message, result=1000, delay=3):
    print(message)
    await asyncio.sleep(delay)
    return resultCode language: Python (python)

The call_api() is a coroutine. It displays a message, pauses a specified number of seconds (default to three seconds), and returns a result.

The following program uses the call_api() twice and measures the time it takes to complete:

import asyncio
import time


async def call_api(message, result=1000, delay=3):
    print(message)
    await asyncio.sleep(delay)
    return result


async def main():
    start = time.perf_counter()

    price = await call_api('Get stock price of GOOG...', 300)
    print(price)

    price = await call_api('Get stock price of APPL...', 400)
    print(price)

    end = time.perf_counter()
    print(f'It took {round(end-start,0)} second(s) to complete.')

asyncio.run(main())Code language: Python (python)

Output:

Get stock price of GOOG...
300
Get stock price of APPL...
400
It took 6.0 second(s) to complete.Code language: Python (python)

How it works (focusing on the main() coroutine):

First, start a timer to measure the time using the perf_counter() function of the time module:

 start = time.perf_counter()Code language: Python (python)

Second, call the call_api() coroutine and display the result:

price = await call_api('Get stock price of GOOG...', 300)
print(price)Code language: Python (python)

Third, call the call_api() a second time:

price = await call_api('Get stock price of APPL...', 400)
print(price)Code language: Python (python)

Finally, show the time the program takes to complete:

end = time.perf_counter()
print(f'It took {round(end-start,0)} second(s) to complete.')Code language: Python (python)

Because each call_api() takes three seconds, and calling it twice takes six seconds.

In this example, we call a coroutine directly and don’t put it on the event loop to run. Instead, we get a coroutine object and use the await keyword to execute it and get a result.

The following picture illustrates the execution flow of the program:

Python create_task Asynchronously

In other words, we use async and await to write asynchronous code but can’t run it concurrently. To run multiple operations concurrently, we’ll need to use something called tasks.

Introduction to Python tasks

A task is a wrapper of a coroutine that schedules the coroutine to run on the event loop as soon as possible.

The scheduling and execution occur in a non-blocking manner. In other words, you can create a task and execute other code instantly while the task is running.

Notice that the task is different from the await keyword that blocks the entire coroutine until the operation completes with a result.

It’s important that you can create multiple tasks and schedule them to run instantly on the event loop at the same time.

To create a task, you pass a coroutine to the create_task() function of the asyncio package. The create_task() function returns a Task object.

The following program illustrates how to create two tasks that schedule and execute the call_api() coroutine:

import asyncio
import time


async def call_api(message, result=1000, delay=3):
    print(message)
    await asyncio.sleep(delay)
    return result


async def main():
    start = time.perf_counter()

    task_1 = asyncio.create_task(
        call_api('Get stock price of GOOG...', 300)
    )

    task_2 = asyncio.create_task(
        call_api('Get stock price of APPL...', 300)
    )

    price = await task_1
    print(price)

    price = await task_2
    print(price)

    end = time.perf_counter()
    print(f'It took {round(end-start,0)} second(s) to complete.')


asyncio.run(main())Code language: Python (python)

Output:

Get stock price of GOOG...
Get stock price of APPL...
300
300
It took 3.0 second(s) to complete.Code language: Python (python)

How it works.

First, start a timer:

start = time.perf_counter()Code language: Python (python)

Next, create a task and schedule it to run on the event loop immediately:

task_1 = asyncio.create_task(
   call_api('Get stock price of GOOG...', 300)
)Code language: Python (python)

Then, create another task and schedule it to run on the event loop immediately:

task_2 = asyncio.create_task(
    call_api('Get stock price of APPL...', 400)
)Code language: Python (python)

After that, wait for the tasks to be completed:

price = await task_1
print(price)

price = await task_2
print(price)Code language: Python (python)

It’s important to use the await keyword to wait for the tasks at some point in the program.

If we did not use the await keyword, Python would schedule the task to run but stopped it when the asyncio.run() shutdown the event loop.

The following picture illustrates the execution flow of the program:

Python create_task Concurrency

Finally, show the time it takes to complete the main() function:

end = time.perf_counter()
print(f'It took {round(end-start,0)} second(s) to complete.')Code language: Python (python)

By using the create_task() function, the program is much faster. The more tasks you run, the faster it is.

Running other tasks while waiting

When the call_api is running, you can run other tasks. For example, the following program displays a message every second while waiting for the call_api tasks:

import asyncio
import time


async def call_api(message, result=1000, delay=3):
    print(message)
    await asyncio.sleep(delay)
    return result


async def show_message():
    for _ in range(3):
        await asyncio.sleep(1)
        print('API call is in progress...')


async def main():
    start = time.perf_counter()

    message_task = asyncio.create_task(
        show_message()
    )

    task_1 = asyncio.create_task(
        call_api('Get stock price of GOOG...', 300)
    )

    task_2 = asyncio.create_task(
        call_api('Get stock price of APPL...', 300)
    )

    price = await task_1
    print(price)

    price = await task_2
    print(price)

    await message_task

    end = time.perf_counter()
    print(f'It took {round(end-start,0)} second(s) to complete.')


asyncio.run(main())Code language: Python (python)

Output:

Get stock price of GOOG...
Get stock price of APPL...
API call is in progress...
API call is in progress...
API call is in progress...
300
300Code language: Python (python)

The following picture illustrates the execution flow:

Summary

  • A task is a wrapper of a coroutine that schedules the coroutine to run on the event loop as soon as possible.
  • Use the create_task() function of the asyncio library to create a task.
  • Use the await keyword with the task at some point in the program so that the task can be completed before the event loop is closed by the asyncio.run() function.
Did you find this tutorial helpful ?