asyncio.gather vs asyncio.wait in Python
Python's asyncio module provides both
asyncio.gather() and asyncio.wait() to run
multiple asynchronous operations concurrently. While both functions
coordinate multiple awaitables, they differ fundamentally in how they
accept inputs, what they return, how they handle task completion, and
the level of control they offer over error handling and task
cancellation. Understanding these distinctions is critical for choosing
the right tool for your concurrency requirements.
1. Input Requirements
asyncio.gather(*aws, return_exceptions=False): Accepts awaitables passed as variable positional arguments (*args). You can pass coroutines directly, andgatherwill automatically wrap them intoTaskobjects.asyncio.wait(fs, *, timeout=None, return_when=ALL_COMPLETED): Accepts a single collection (such as asetorlist) ofTaskorFutureobjects. Passing raw coroutine objects directly toasyncio.wait()is deprecated; they should be explicitly wrapped in tasks first (e.g., usingasyncio.create_task()).
2. Return Values and Ordering
asyncio.gather(): Returns a single list containing the results of each awaitable. Crucially, the returned results preserve the exact order of the awaitables passed in, regardless of which task finishes first.asyncio.wait(): Returns a tuple of two sets:(done, pending). Thedoneset contains tasks that have finished, while thependingset contains tasks that are still running. It does not return the results directly; you must iterate over thedoneset and call.result()on each task. Because sets are unordered, completion order is not inherently mapped to input order.
3. Completion Conditions
asyncio.gather(): Always waits for all awaitables to complete before returning (unless an exception is raised and not captured).asyncio.wait(): Provides fine-grained execution control via thereturn_whenparameter:ALL_COMPLETED(default): Returns when all futures have finished or been cancelled.FIRST_COMPLETED: Returns as soon as any future finishes or is cancelled.FIRST_EXCEPTION: Returns as soon as any future raises an exception, or when all futures succeed.
asyncio.wait() also supports an optional
timeout argument. If the timeout expires before the
condition is met, it returns immediately with whichever tasks are
finished in done and the rest in pending.
4. Exception Handling
asyncio.gather():- If
return_exceptions=False(default), the first unhandled exception raised by any task immediately propagates to the caller. The other tasks continue running in the background. - If
return_exceptions=True, exceptions are captured and treated as successful return values, placed in the result list alongside normal results.
- If
asyncio.wait(): Does not raise exceptions directly upon completion. If a task fails, the task is simply placed in thedoneset. Callingtask.result()on a failed task will raise the exception, or you can inspect it safely usingtask.exception().
Summary: When to Use Which
- Use
asyncio.gather()when you have a collection of independent tasks, need their results returned in a predictable order, and want a simple, high-level API. - Use
asyncio.wait()when you need lower-level control over execution flow, such as processing tasks as they finish (FIRST_COMPLETED), cancelling leftover work upon the first failure, or enforcing execution timeouts across a batch of tasks.