Python asyncio.wait() function (with examples)

Updated: July 26, 2023 By: Wolf Post a comment

The Fundamentals

In Python, the asyncio.wait() function is a high-level asyncio API that allows you to run an iterable of awaitable objects concurrently and wait for them to complete. An awaitable object is an object that can be used in an await expression, such as a coroutine, a Task, or a Future.

The asyncio.wait() function was added to the Python standard library in version 3.4, as part of the asyncio module. The asyncio module provides infrastructure for writing concurrent code using the async/await syntax.

Syntax

The syntax of the asyncio.wait() function is as follows:

asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)

Where:

  • aws: an iterable of awaitable objects that you want to run concurrently.
  • timeout: an optional number of seconds to wait before returning. If None (the default), wait until all awaitables are done or canceled.
  • return_when: an optional constant that indicates when the function should return. It can be one of the following values:
    • asyncio.FIRST_COMPLETED: return when any awaitable is done or canceled.
    • asyncio.FIRST_EXCEPTION: return when any awaitable is done by raising an exception. If no awaitable raises an exception, this is equivalent to asyncio.ALL_COMPLETED.
    • asyncio.ALL_COMPLETED: return when all awaitables are done or canceled.

The asyncio.wait() function returns two sets: done and pending. The done set contains the awaitables that are done or canceled. The pending set contains the awaitables that are still pending.

When should or shouldn’t you use asyncio.wait()?

You can use the asyncio.wait() function when you need to run multiple coroutines or tasks concurrently and wait for them to finish. For instance, you can use it to perform parallel network requests, process data in parallel, or implement timeouts.

You should not use the asyncio.wait() function when you need to run coroutines or tasks sequentially, or when you don’t need to wait for them to finish. In those situations, you can use other asyncio APIs, such as asyncio.run(), asyncio.create_task(), or asyncio.gather().

Examples

Some practical code examples that can help you understand the asyncio.wait() function deeper, and better.

Basic example

This example demonstrates how to use the asyncio.wait() function to run two coroutines concurrently and wait until both of them are done. The coroutines print some messages and sleep for a random amount of time.

import asyncio
import random

async def say_hello(name):
    print(f"Hello, {name}!")
    await asyncio.sleep(random.randint(1, 3))
    print(f"Goodbye, {name}!")
    return f"Done with {name}!"

async def main():
    # create two tasks
    task1 = asyncio.create_task(say_hello("Sling Academy"))
    task2 = asyncio.create_task(say_hello("Mr. Wolf"))

    # wait for both tasks to finish
    done, pending = await asyncio.wait({task1, task2})

    # print the results
    for task in done:
        print(f"Task result: {task.result()}")

# run the main coroutine
asyncio.run(main())

Output:

Hello, Sling Academy!
Hello, Mr. Wolf!
Goodbye, Sling Academy!
Goodbye, Mr. Wolf!
Task result: Done with Mr. Wolf!
Task result: Done with Sling Academy!

Intermediate example

This example shows how to use the asyncio.wait() function with the return_when parameter to control when the function returns. The return_when parameter can be one of the following constants: asyncio.FIRST_COMPLETED, asyncio.FIRST_EXCEPTION, or asyncio.ALL_COMPLETED. The example also shows how to handle exceptions raised by the awaitable objects.

import asyncio


async def divide(x, y):
    print(f"Dividing {x} by {y}")
    await asyncio.sleep(3)
    if y == 0:
        raise ZeroDivisionError(f"Error dividing {x} by {y}")
    return f"{x} / {y} = {x/y}"


async def main():
    # create three tasks
    task1 = asyncio.create_task(divide(10, 2))
    task2 = asyncio.create_task(divide(15, 0))
    task3 = asyncio.create_task(divide(20, 5))

    # wait for the first task to finish or raise an exception
    done, pending = await asyncio.wait(
        {task1, task2, task3}, return_when=asyncio.FIRST_EXCEPTION
    )

    # cancel the pending tasks
    for task in pending:
        task.cancel()

    # print the results or handle the exceptions
    for task in done:
        try:
            print(f"Task result: {task.result()}")
        except ZeroDivisionError as e:
            print(f"Task error: {e}")


# run the main coroutine
asyncio.run(main())

Output:

Dividing 10 by 2
Dividing 15 by 0
Dividing 20 by 5
Task error: Error dividing 15 by 0
Task result: 10 / 2 = 5.0
Task result: 20 / 5 = 4.0

Advanced example

In this example, we’ll use the asyncio.wait() function with a timeout parameter to limit the maximum amount of time to wait for the awaitable objects. The timeout parameter can be an int or a float that represents the number of seconds to wait. You’ll also see how to use the loop.time() method to measure the elapsed time.

import asyncio


async def count():
    print("Hello buddy, welcome to Sling Academy!")
    await asyncio.sleep(3)
    print("Goodbye buddy, see you soon!")
    return "Done"


async def main():
    # get the current event loop
    loop = asyncio.get_running_loop()

    # get the current time
    start = loop.time()

    # create three tasks
    task1 = loop.create_task(count())
    task2 = loop.create_task(count())
    task3 = loop.create_task(count())

    # wait for at most 2 seconds
    done, pending = await asyncio.wait({task1, task2, task3}, timeout=2)

    # print the results or cancel the pending tasks
    for task in done:
        print(f"Task result: {task.result()}")

    for task in pending:
        print(f"Task cancelled: {task.cancel()}")

    # get the elapsed time
    end = loop.time()

    print(f"Time elapsed: {end - start:.2f} seconds")


# run the main coroutine
asyncio.run(main())

Output:

Hello buddy, welcome to Sling Academy!
Hello buddy, welcome to Sling Academy!
Hello buddy, welcome to Sling Academy!
Task cancelled: True
Task cancelled: True
Task cancelled: True
Time elapsed: 2.00 seconds

This tutorial ends here. Happy coding & enjoy your journey with modern Python!