A task that is scheduled or suspended will be assigned an internal state of “pending“.
In this tutorial, you will discover pending asyncio tasks in Python.
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:
1 2 3 |
... # 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 a pending task.
Run loops using all CPUs, download your FREE book to learn how.
What is a Pending Task
A pending asyncio task is a task that is not currently running.
Technically, a pending task is a running task.
We can determine if a task is running by calling the done() method on an asyncio.Task object, which will return False.
For example:
1 2 3 4 |
... # check if a task is running if not task.done(): # ... |
While running, a task may be pending.
We cannot check if a task is pending directly. There is no method on an asyncio.Task object to check for this state.
Instead, we can print a Task object and the string representation of a task will indicate that it is “running” and potentially that it is “pending“.
A task may not be pending for two main reasons, they are:
- The task has been created and scheduled but has not yet run.
- The task is awaiting another task or coroutine.
Let’s take a closer look at these two cases.
Pending Scheduled Task
A scheduled task is a task that has been created and is scheduled in the event loop but has not yet had an opportunity to execute.
This will occur immediately after creating a task within another task or within a coroutine.
The new task will not run at least until the task or coroutine that created it is done or suspended.
For example:
1 2 3 4 5 |
... # create and schedule a task task = asyncio.create_task(...) # ... # the task is scheduled, but not yet executing |
Technically, a scheduled task is running.
Because it is not running yet, it has the internal status of “pending“.
Pending Running Task
A task can suspend itself.
This can be achieved if it awaits another task or coroutine.
This may be explicit via the await expression.
For example:
1 2 3 |
... # suspend this running task await asyncio.sleep(1) |
It may be less obvious, such as if the task executes an asynchronous generator, iterator, or context manager.
For example:
1 2 3 4 |
... # suspend this running task async for item in items: # ... |
When a task is suspended, it will be marked internally as “pending“.
Now that we know about pending asyncio tasks, let’s look at some examples.
Example of Pending Scheduled Task
A scheduled task has an internal state of pending.
We can explore this with a worked example.
In this example, we will create a new task from a custom coroutine that blocks for a moment. After the task is created and scheduled, before it begins executing, we will report its details.
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 |
# SuperFastPython.com # example of a pending scheduled task import asyncio # define a coroutine for a task async def task_coroutine(): # block for a moment await asyncio.sleep(1) # main coroutine async def main(): # create and schedule the task task = asyncio.create_task(task_coroutine()) # report status print(task) # wait for the task to complete await task # start the asyncio program asyncio.run(main()) |
Running the example first creates the main coroutine and uses it to start the asyncio program.
The main() coroutine runs. It creates a new task coroutine and uses it to create and schedule a new task.
It then immediately reports the details of the task, before waiting for the task to be completed.
We can see that the task has an internal state of “pending” and “running“.
1 |
<Task pending name='Task-2' coro=<task_coroutine() running at ...>> |
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.
Example of Pending Running Task
A suspended task has an internal state of pending.
We can explore this with a worked example.
In this example, we will create a new task from a custom coroutine that blocks for a moment. The main task will block for a moment to allow the task to begin executing. It will then resume and report the details of the suspended task.
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 |
# SuperFastPython.com # example of a pending running task import asyncio # define a coroutine for a task async def task_coroutine(): # block for a moment await asyncio.sleep(1) # main coroutine async def main(): # create and schedule the task task = asyncio.create_task(task_coroutine()) # allow the task to run a moment await asyncio.sleep(0.5) # report status print(task) # wait for the task to complete await task # start the asyncio program asyncio.run(main()) |
Running the example first creates the main coroutine and uses it to start the asyncio program.
The main() coroutine runs. It creates a new task coroutine and uses it to create and schedule a new task.
The main() coroutine then suspends for a moment to allow the task to execute.
The task executes and immediately suspends itself with a call to sleep.
The main() coroutine resumes and reports the details of the task, before waiting for the task to be completed.
As with the scheduled task, we can see that the task has an internal state of “pending” and “running“.
Unlike the scheduled task, we can see that the pending task is explicitly waiting to resume.
1 |
<Task pending name='Task-2' coro=<task_coroutine() running at ...> wait_for=<Future pending cb=[Task.task_wakeup()]>> |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Further Reading
This section provides additional resources that you may find helpful.
Python Asyncio Books
- Python Asyncio Mastery, Jason Brownlee (my book!)
- Python Asyncio Jump-Start, Jason Brownlee.
- Python Asyncio Interview Questions, Jason Brownlee.
- Asyncio Module API Cheat Sheet
I also recommend the following books:
- Python Concurrency with asyncio, Matthew Fowler, 2022.
- Using Asyncio in Python, Caleb Hattingh, 2020.
- asyncio Recipes, Mohamed Mustapha Tahrioui, 2019.
Guides
APIs
- asyncio — Asynchronous I/O
- Asyncio Coroutines and Tasks
- Asyncio Streams
- Asyncio Subprocesses
- Asyncio Queues
- Asyncio Synchronization Primitives
References
Takeaways
You now know about pending asyncio tasks in Python.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Remy Lovesy on Unsplash
Do you have any questions?