How to Check Asyncio Task Status - Super Fast Python

An asyncio Task is an object that schedules and independently runs an asyncio coroutine.

It provides a handle on a scheduled coroutine that an asyncio program can query and use to interact with the coroutine.

Because tasks are executed asynchronously, we often need to check on the status of the task, such as whether it is done, is still running, or was cancelled.

In this tutorial, you will discover how to check the status of an asyncio task.

After completing this tutorial, you will know:

  • How to check if an asyncio task is running or is done.
  • How to check if a task was canceled or completed normally.
  • Understand the conditions under which we may consider that a task is done.

Let’s get started.

What is an Asyncio Task

An asyncio Task is an object that schedules and independently runs an asyncio coroutine.

It provides a handle on a scheduled coroutine that an asyncio program can query and use to interact with the coroutine.

A Task is an object that manages an independently running coroutine.

PEP 3156 – Asynchronous IO Support Rebooted: the “asyncio” Module

An asyncio task is represented via an instance of the asyncio.Task class.

A task is created from a coroutine. It requires a coroutine object, wraps the coroutine, schedules it for execution, and provides ways to interact with it.

A task is executed independently. This means it is scheduled in the asyncio event loop and will execute regardless of what else happens in the coroutine that created it. This is different from executing a coroutine directly, where the caller must wait for it to complete.

Tasks are used to schedule coroutines concurrently. When a coroutine is wrapped into a Task with functions like asyncio.create_task() the coroutine is automatically scheduled to run soon

Coroutines and Tasks

We can create a task using the asyncio.create_task() function.

This function takes a coroutine instance and an optional name for the task and returns an asyncio.Task instance.

Wrap the coro coroutine into a Task and schedule its execution. Return the Task object.

Coroutines and Tasks

For example:

...

# create and schedule a task

task = asyncio.create_task(coro)

You can learn more about asyncio tasks in the tutorial:

Now that we know about asyncio tasks, let’s look at how we might check their status.

After a Task is created, we can check the status of the task.

There are two statuses we might want to check, they are:

  1. Whether the task is done.
  2. Whether the task was canceled.

Let’s take a closer look at each in turn.

Check if a Task is Done

We can check if a task is done via the done() method.

The method returns True if the task is done, or False otherwise.

For example:

...

# check if a task is done

if task.done():

# ...

A task is done if it has had the opportunity to run and is now no longer running.

A task that has been scheduled is not done.

Similarly, a task that is running is not done.

Check if a Task is Canceled

We can check if a task is canceled via the cancelled() method.

The method returns True if the task was canceled, or False otherwise.

For example:

...

# check if a task was canceled

if task.cancelled():

# ...

A task is canceled if the cancel() method was called on the task and completed successfully, e.g. cancel() returned True.

A task is not canceled if the cancel() method was not called, or if the cancel() method was called but failed to cancel the task.

When is an Asyncio Task Done?

A done task means the task is finished.

It means that the task had at least one opportunity to execute and is no longer able to execute.

A task is done if:

  1. The coroutine finishes normally.
  2. The coroutine returns explicitly.
  3. An unexpected error or exception is raised in the coroutine
  4. The task is canceled.

A scheduled task is not done.

A suspended task is not done.

A sleeping task is not done.

Now that we know how to check the status of a task, let’s look at some worked examples.


Free Python Asyncio Course

Download your FREE Asyncio PDF cheat sheet and get BONUS access to my free 7-day crash course on the Asyncio API.

Discover how to use the Python asyncio module including how to define, create, and run new coroutines and how to use non-blocking I/O.

Learn more
 


Example of Checking if a Task is Done

We can explore how to check if a task is done.

In this example, we will define a task coroutine that reports a message and sleeps for a moment. We will then create and schedule the task coroutine from the main coroutine.

We will check the status of the task immediately after it is created before it has run. We will then give the task an opportunity to run, then check the status of the task while it is running. Finally, we will check the status of the task after it is finished

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

# SuperFastPython.com

# example of checking if an asyncio task is done

import asyncio

# define a coroutine for a task

async def task_coroutine():

    # report a message

    print('executing the task')

    # block for a moment

    await asyncio.sleep(1)

# custom coroutine

async def main():

    # create and schedule the task

    task = asyncio.create_task(task_coroutine())

    # check if it is done

    print(f'>task done: {task.done()}')

    # wait a moment

    await asyncio.sleep(0.1)

    # check if it is done

    print(f'>task done: {task.done()}')

    # wait for the task to complete

    await task

    # check if it is done

    print(f'>task done: {task.done()}')

# start the asyncio program

asyncio.run(main())

Running the example first creates the main() coroutine and runs it as the entry point to the asyncio program.

The main() coroutine runs, first creating an instance of the task_coroutine() coroutine and then using it to create and schedule a task.

The main() coroutine then checks if the new task is done, which it is not because it has just been scheduled. It then sleeps, giving the task an opportunity to run.

The task begins executing, reporting a message, and then sleeping.

The main() coroutine then resumes and checks if the coroutine is done. At this point, the task_coroutine() is still running, yet suspended in a call to sleep.

The main() coroutine then awaits the task to be completed.

The task_coroutine() finishes its sleep, resumes, and then terminates.

The main() coroutine then continues on and checks the done status one final time. This time the task is done as it has completely finished.

This example highlights that a scheduled and suspended task is not done and that a task is not done until the wrapped coroutine is exited.

>task done: False

executing the task

>task done: False

>task done: True

Next, let’s explore how we might poll the done status of a task.

Example of Polling a Task Until it is Done

We can explore how to poll the status of a task in a busy-wait loop.

In this example, we will create and schedule a task as before. The main coroutine will loop until the task is complete, each iteration checking and reporting the status of the task and sleeping for a moment if the task is not yet complete.

It is critical that the main coroutine suspend each iteration in order to give other coroutines an opportunity to execute, e.g. to allow the task coroutine an opportunity to run and complete.

Busy wait loops are not a good practice because they consume more resources than simply waiting for an event, but they do give the caller to perform other actions while waiting.

You can learn more about busy wait loops in the tutorial:

The complete example of a busy wait loop for checking the status of an asyncio task is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

# SuperFastPython.com

# example of polling the status of an asyncio task until it is done

import asyncio

# define a coroutine for a task

async def task_coroutine():

    # report a message

    print('executing the task')

    # block for a moment

    await asyncio.sleep(1)

# custom coroutine

async def main():

    # create and schedule the task

    task = asyncio.create_task(task_coroutine())

    # poll the status of the task

    while True:

        # get the status of the task

        status = task.done()

        # report the status

        print(f'>task done: {task.done()}')

        # check if the task is done

        if status:

            break

        # otherwise block for a moment

        await asyncio.sleep(0.1)

# start the asyncio program

asyncio.run(main())

Running the example first creates the main() coroutine and runs it as the entry point to the asyncio program.

The main() coroutine runs, first creating an instance of the task_coroutine() coroutine and then using it to create and schedule a task.

It then begins to loop forever.

In each iteration, the main() coroutine gets the done status of the task, reports the status, then checks the status. If the task is done, the loop is broken, otherwise, it suspends, sleeping for a fraction of a second.

The task runs, reports its message, and sleeps. Finally, it terminates.

The main() coroutine notices that the task is done, then exits the loop.

>task done: False

executing the task

>task done: False

>task done: False

>task done: False

>task done: False

>task done: False

>task done: False

>task done: False

>task done: False

>task done: False

>task done: True

Next, let’s explore the done status of a task that fails with an exception.


Python Asyncio Jump-Start

Loving The Tutorials?

Why not take the next step? Get the book.

Learn more
 


Example of an Exception Causes a Task to be Done

We can explore the status of a task where the wrapped coroutine fails with an exception.

In this example, we will update the task coroutine to sleep for less time and to then raise an exception.

This will terminate the task and cause the task to be marked as done.

The complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

# SuperFastPython.com

# example of checking the status of a task that raises an exception

import asyncio

# define a coroutine for a task

async def task_coroutine():

    # report a message

    print('executing the task')

    # block for a moment

    await asyncio.sleep(0.5)

    # raise an exception

    raise Exception('Something bad happened')

# custom coroutine

async def main():

    # create and schedule the task

    task = asyncio.create_task(task_coroutine())

    # check if it is done

    print(f'>task done: {task.done()}')

    # wait a moment

    await asyncio.sleep(0.1)

    # check if it is done

    print(f'>task done: {task.done()}')

    # wait for the task to complete

    await asyncio.sleep(0.5)

    # check if it is done

    print(f'>task done: {task.done()}')

# start the asyncio program

asyncio.run(main())

Running the example first creates the main() coroutine and runs it as the entry point to the asyncio program.

The main() coroutine runs, first creating an instance of the task_coroutine() coroutine and then using it to create and schedule a task.

The main() coroutine then checks if the new task is done, which it is not because it has just been scheduled. It then sleeps, giving the task an opportunity to run.

The task begins executing, reporting a message, and then sleeping.

The main() coroutine then resumes and checks if the coroutine is done. At this point, the task_coroutine() is still running, yet suspended in a call to sleep.

The main() coroutine then sleeps for a little longer. We don’t await the task, because it will raise an exception that will need to be handled in our main() coroutine.

The task_coroutine() finishes its sleep and then resumes raising an exception. This terminates the task and marks it as done.

The main() coroutine then continues on and checks the done status one final time. This time the task is done.

The event loop is exited and notices that an exception was raised in a task that was never retrieved. It then logs the exception using a default logger, e.g. to standard output.

This example highlights that an exception raised in a coroutine wrapped in a task will cause the task to be done.

>task done: False

executing the task

>task done: False

>task done: True

Task exception was never retrieved

future: <Task finished name='Task-2' coro=<task_coroutine() done, defined at ...> exception=Exception('Something bad happened')>

Traceback (most recent call last):

  File "...", line 12, in task_coroutine

    raise Exception('Something bad happened')

Exception: Something bad happened

Next, we can look at the done status of a canceled task.

Example of Being Canceled Causes a Task to be Done

A canceled task is marked as done.

We can explore canceling a task and then checking if it is indeed done.

A task can be canceled by calling the cancel() method.

In this example, we create and schedule the task as before. We wait a moment and check its done status while running, then cancel the task. The main coroutine waits for the task to be canceled completely and then checks the done status a final time.

The expectation is a task that is canceled and is given an opportunity to be canceled (have a CancelledError exception raised within the wrapped coroutine) will be canceled.

The complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

# SuperFastPython.com

# example of canceling a task and checking its done status

import asyncio

# define a coroutine for a task

async def task_coroutine():

    # report a message

    print('executing the task')

    # block for a moment

    await asyncio.sleep(1)

# custom coroutine

async def main():

    # create and schedule the task

    task = asyncio.create_task(task_coroutine())

    # check if it is done

    print(f'>task done: {task.done()}')

    # wait a moment

    await asyncio.sleep(0.1)

    # check if it is done

    print(f'>task done: {task.done()}')

    # cancel the task

    task.cancel()

    # wait a moment more

    await asyncio.sleep(0.1)

    # check if it is done

    print(f'>task done: {task.done()}')

# start the asyncio program

asyncio.run(main())

Running the example first creates the main() coroutine and runs it as the entry point to the asyncio program.

The main() coroutine runs, first creating an instance of the task_coroutine() coroutine and then using it to create and schedule a task.

The main() coroutine then checks if the new task is done, which it is not because it has just been scheduled. It then sleeps, giving the task an opportunity to run.

The task begins executing, reporting a message, and then sleeping.

The main() coroutine then resumes and checks if the coroutine is done. At this point, the task_coroutine() is still running, yet suspended in a call to sleep.

The main() coroutine then cancels the task via a call to the cancel() method.

The main() coroutine then sleeps for a little longer. We don’t await the task, because it will raise a CancelledError exception which will need to be handled in our main() coroutine.

The task_coroutine() is canceled. This terminates the task and marks it as done.

The main() coroutine then resumes and checks the done status one final time. This time the task is done.

>task done: False

executing the task

>task done: False

>task done: True

Next, let’s look at how we might check if a task is canceled.

Example of Checking if a Task is Canceled

We can explore how to check if an asyncio task is canceled.

A task is only canceled if a coroutine calls the cancel() method.

A normal task that is not canceled will not have the status of canceled.

The example below demonstrates this.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# SuperFastPython.com

# example of checking if an asyncio task is canceled

import asyncio

# define a coroutine for a task

async def task_coroutine():

    # report a message

    print('executing the task')

    # block for a moment

    await asyncio.sleep(1)

# custom coroutine

async def main():

    # create and schedule the task

    task = asyncio.create_task(task_coroutine())

    # check if it is canceled

    print(f'>task canceled: {task.cancelled()}')

    # wait for the task to complete

    await task

    # check if it is canceled

    print(f'>task canceled: {task.cancelled()}')

# start the asyncio program

asyncio.run(main())

Running the example first creates the main() coroutine and runs it as the entry point to the asyncio program.

The main() coroutine runs, first creating an instance of the task_coroutine() coroutine and then using it to create and schedule a task.

The main() coroutine then checks if the new task is canceled, which it is not because it has just been scheduled. It then awaits the task to be done.

The task begins executing, reporting a message, and then sleeping. The task_coroutine() finishes its sleep, resumes, and then terminates.

The main() coroutine resumes and checks the canceled status a final time. This time the task is done, but is not canceled.

This example highlights that a done task does not have the canceled status.

>task canceled: False

executing the task

>task canceled: False

Next, let’s look at an example of canceling a task and confirming it has a canceled status.

Example of Checking if a Canceled Task was Canceled

We can explore the case of explicitly canceling a task, then confirming that the status of the task is marked as canceled.

In this example, we update the previous example to cancel the task while it is running, wait for the task to be done after being canceled, then check its status.

The complete example is listed below.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

# SuperFastPython.com

# example of canceling a task then checking if it is canceled

import asyncio

# define a coroutine for a task

async def task_coroutine():

    # report a message

    print('executing the task')

    # block for a moment

    await asyncio.sleep(1)

# custom coroutine

async def main():

    # create and schedule the task

    task = asyncio.create_task(task_coroutine())

    # check if it is canceled

    print(f'>task canceled: {task.cancelled()}')

    # give the task a chance to run

    await asyncio.sleep(0.1)

    # cancel the task

    task.cancel()

    # wait for the task to be done

    await asyncio.sleep(0.1)

    # check if it is canceled

    print(f'>task canceled: {task.cancelled()}')

# start the asyncio program

asyncio.run(main())

Running the example first creates the main() coroutine and runs it as the entry point to the asyncio program.

The main() coroutine runs, first creating an instance of the task_coroutine() coroutine and then using it to create and schedule a task.

The main() coroutine then checks if the new task is canceled, which it is not because it has just been scheduled. It is then suspended, sleeping for a fraction of a section.

This gives the task an opportunity to execute, report its message and sleep itself.

The main() coroutine resumes and then cancels the task.

It then sleeps again, suspending and allowing the task to execute and handle the request to be canceled.

The task resumes and completes its cancellation, e.g. a CancelledError exception is raised in the coroutine and is not handled. The task is both done and marked as canceled.

The main() coroutine then checks the canceled status one final time, confirming that indeed the done task is now marked as canceled.

>task canceled: False

executing the task

>task canceled: True

Further Reading

This section provides additional resources that you may find helpful.

Python Asyncio Books

I also recommend the following books:

Guides

APIs

References

Takeaways

You now know how to check the status of an asyncio task in Python.

Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.

Photo by Goh Rhy Yan on Unsplash