What is an Asyncio Awaitable in Python - Super Fast Python

A coroutine is an awaitable that can be wrapped in a Task. A coroutine can pause execution and wait for an awaitable object to be done via the await keyword.

In this tutorial, you will discover asyncio awaitables and how to create and use them in Python.

Let’s get started.

An awaitable is a Python object that can be waited on using the “await” keyword.

We say that an object is an awaitable object if it can be used in an await expression.

Coroutines and Tasks

For example:

...

# await an awaitable

await awaitable

An awaitable allows a coroutine in asyncio to pause execution and wait for the specified awaitable to be done.

A done coroutine means that the coroutine finished normally, returned explicitly, was canceled, or failed with an error or exception.

There are three main types of awaitables that may work within our asyncio programs, they are:

  1. Coroutine
  2. Future
  3. Task

Coroutines are awaitable, which means that a coroutine may wait on another coroutine.

A task is a coroutine that is wrapped and executed independently. The task provides a handle on the independently exciting coroutine, allowing us to check its status or get the result when needed. We may also wait for it to complete.

Tasks are used to schedule coroutines concurrently.

Coroutines and Tasks

The Task class extends the Future class, which is also awaitable.

Generally, we do not create Future objects as part of the normal use of the asyncio module.

A Future is a special low-level awaitable object that represents an eventual result of an asynchronous operation. […] Normally there is no need to create Future objects at the application level code.

Coroutines and Tasks

Now that we know what an awaitable is, let’s look at how we might create them.

How to Create an Awaitable?

There are two main ways to create an awaitable, they are:

  1. Create a Coroutine
  2. Create a Task

There are other ways, such as creating custom Future objects, but we will not consider them in this tutorial.

Let’s take a closer look at each of these examples.

Create a Coroutine

A coroutine is defined like a function (like a routine).

The important difference is that it has the “async” keyword before the “def” keyword in the function definition.

For example:

# define a coroutine

async def custom_funcion():

# ...

We can create an awaitable from a coroutine definition by creating an instance of the coroutine.

This looks like calling the coroutine, but in fact, returns a coroutine object.

For example:

...

# create the coroutine

coro = custom_coroutine()

This creates an instance of the coroutine which is an awaitable.

A coroutine may then await it directly.

For example:

...

# await the custom coroutine

await coro

This is typically performed in a single line.

For example:

...

# await the custom coroutine

await custom_coroutine()

Create a Task

A Task wraps a coroutine and will schedule the coroutine for execution, when possible, and provides a handle on the coroutine.

As such, a task is created using a coroutine. This can be achieved using the asyncio.create_task() function.

For example:

...

# create the coroutine

coro = custom_coroutine()

# create and schedule a coroutine as a task

task = asyncio.create_task(coro)

The task will execute whenever possible.

Nevertheless, a coroutine may pause execution and wait for the task to complete because it is an awaitable.

For example:

...

# await the custom coroutine wrapped in a task

await task

This may all be achieved in one compound statement, which can be most confusing to beginners.

For example:

...

# await the custom coroutine wrapped in a task

await asyncio.create_task(custom_coroutine())

Now that we know how to create awaitables, let’s look at some worked examples.

Example of Coroutine Awaitable

We can explore a coroutine as an awaitable.

In this example, we will run a coroutine that will create another coroutine which is then awaited. This both schedules the new coroutine for execution and pauses the execution of the current coroutine until the new coroutine is done.

The complete example is listed.

# SuperFastPython.com

# example of a coroutine as an awaitable

import asyncio

# definition of another coroutine

async def another_coroutine():

    print('Another coroutine')

# define a custom coroutine

async def custom_coroutine():

    print('hello world')

    # await another coroutine

    await another_coroutine()

# start the asyncio program

asyncio.run(custom_coroutine())

Running the example first creates the custom coroutine, then uses it to start the asyncio event loop.

The coroutine executes and reports a message. I then create a second coroutine and schedule it for execution. This pauses the execution of the first coroutine until the second coroutine is done.

The second coroutine runs and reports a message, then is done.

The first coroutine then continues on and is also done.

This highlights how we may treat a coroutine as an awaitable and await it from within another coroutine.

hello world

Another coroutine


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 Task Awaitable

We can explore a Task as an awaitable.

In this example, we will run a coroutine that will create a second coroutine that will be wrapped in a task. The first coroutine will then await the task, treating it directly as an awaitable.

The complete example is listed below.

# SuperFastPython.com

# example of a task as an awaitable

import asyncio

# definition of a another coroutine

async def another_coroutine():

    print('Another coroutine')

# define a custom coroutine

async def custom_coroutine():

    print('hello world')

    # await a task

    await asyncio.create_task(another_coroutine())

# start the asyncio program

asyncio.run(custom_coroutine())

Running the example creates a coroutine and starts the asyncio program.

The coroutine executes and reports a message. It then creates a second coroutine, wraps it in a task, and then awaits it.

This schedules the independent second coroutine for execution in the asyncio event loop and pauses the execution of the first coroutine until the awaitable is complete.

The task executes, in turn executing the second coroutine that reports a message.

The task completes and returns control to the first coroutine when it finishes.

This highlights how we might create a task and treat it as an awaitable within a coroutine.

hello world

Another coroutine

Further Reading

This section provides additional resources that you may find helpful.

Python Asyncio Books

I also recommend the following books:

Guides

APIs

References


Python Asyncio Jump-Start

Loving The Tutorials?

Why not take the next step? Get the book.

Learn more
 


Takeaways

You now know what awaitables are and how to create and use them in Python.

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

Photo by Jakob Rosen on Unsplash