Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Learn asyncio.wait() and Task Control | Tasks and Scheduling
Python Asyncio in Depth

asyncio.wait() and Task Control

Swipe to show menu

asyncio.gather() is convenient but opinionated – it waits for all coroutines and returns results in input order. asyncio.wait() gives you more control: you decide when to stop waiting and what to do with each task individually.

Basic Usage

asyncio.wait() takes a set of tasks (not coroutines) and returns two sets: done and pending.

12345678910111213141516171819202122232425
import asyncio import httpx import nest_asyncio nest_asyncio.apply() # Fetching posts and processing them as they complete async def fetch_post(client, post_id): url = f"https://jsonplaceholder.typicode.com/posts/{post_id}" response = await client.get(url) return response.json()["title"] async def main(): async with httpx.AsyncClient() as client: tasks = { asyncio.create_task(fetch_post(client, post_id)) for post_id in [1, 2, 3] } done, pending = await asyncio.wait(tasks) for task in done: print(task.result()) asyncio.run(main())

Unlike gather(), results arrive in completion order – whichever task finishes first is processed first.

Return Conditions

The return_when parameter controls when wait() returns:

  • asyncio.ALL_COMPLETED (default): waits for all tasks to finish;
  • asyncio.FIRST_COMPLETED: returns as soon as any one task finishes;
  • asyncio.FIRST_EXCEPTION: returns as soon as any task raises an exception.
1234567891011121314151617181920212223242526272829303132
import asyncio import httpx import nest_asyncio nest_asyncio.apply() # Returning as soon as the first post is fetched async def fetch_post(client, post_id): url = f"https://jsonplaceholder.typicode.com/posts/{post_id}" response = await client.get(url) return response.json()["title"] async def main(): async with httpx.AsyncClient() as client: tasks = { asyncio.create_task(fetch_post(client, post_id)) for post_id in [1, 2, 3] } done, pending = await asyncio.wait( tasks, return_when=asyncio.FIRST_COMPLETED, ) print(f"First result: {next(iter(done)).result()}") print(f"Still pending: {len(pending)} tasks") # Cancelling remaining tasks for task in pending: task.cancel() asyncio.run(main())

Cancelling Tasks

Any task can be cancelled with task.cancel(). The task receives a CancelledError at its next await point.

1234567891011121314151617181920212223242526
import asyncio import nest_asyncio nest_asyncio.apply() async def slow_work(label): try: print(f"Starting {label}") await asyncio.sleep(5) # Simulating slow I/O return f"Done {label}" except asyncio.CancelledError: print(f"{label} was cancelled") raise # Re-raising is required async def main(): task = asyncio.create_task(slow_work("task_A")) await asyncio.sleep(1) # Letting the task start task.cancel() try: await task except asyncio.CancelledError: print("Task cancelled successfully") asyncio.run(main())

Always re-raise CancelledError inside a coroutine – swallowing it silently breaks cancellation propagation.

gather() vs wait()

question mark

What does asyncio.wait() return?

Select the correct answer

Everything was clear?

How can we improve it?

Thanks for your feedback!

Section 2. Chapter 3

Ask AI

expand

Ask AI

ChatGPT

Ask anything or try one of the suggested questions to begin our chat

Section 2. Chapter 3
some-alt