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 toasyncio.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!