How to Run Coroutines Concurrently with asyncio.gather in Python
Run multiple async coroutines concurrently and collect their results in the order they were passed.
Python code
22 linesimport asyncio
async def fetch_data(name: str, delay: float) -> str:
"""Simulate an async operation (e.g., API call) with a delay."""
await asyncio.sleep(delay)
return f"{name} data (after {delay}s)"
async def main() -> None:
"""Run multiple coroutines concurrently with asyncio.gather."""
results = await asyncio.gather(
fetch_data("alpha", 2.0),
fetch_data("beta", 1.0),
fetch_data("gamma", 1.5),
)
for item in results:
print(item)
if __name__ == "__main__":
asyncio.run(main())
Output
alpha data (after 2.0s)
beta data (after 1.0s)
gamma data (after 1.5s)
How it works
asyncio.gather schedules all coroutines concurrently on the event loop, then awaits them together. The returned list preserves the original call order, not completion order — alpha finishes last but appears first. Each fetch_data coroutine suspends on asyncio.sleep, letting the loop switch to another coroutine. asyncio.run(main()) creates and closes the event loop for the whole program. This pattern cuts total wall time from ~4.5 seconds to ~2 seconds versus running sequentially.
Common mistakes
- Forgetting `await` before `asyncio.gather(...)`, which wraps a coroutine without running it
- Expecting results in completion order instead of call order
- Passing a list directly (`*my_list` needed to unpack coroutines)
- Ignoring the `return_exceptions=True` option when one task may fail
Variations
- Use `asyncio.gather(*[fetch_data(n, d) for n, d in jobs])` with a list of coroutines
- Switch to `asyncio.wait` or `asyncio.as_completed` when you need fine-grained control over results
Real-world use cases
- Batching independent API or database calls — fetch a user's profile, orders, and preferences in parallel.
- Fanning out HTTP requests to multiple third-party endpoints and combining their responses for one aggregated view.
- Running parallel health checks across several microservices and reporting results in a consistent order.
Sponsored
More from Concurrency & performance
- Benchmark list.append vs deque.append in Python medium
- Build a Python Performance Profiler That Generates Readable Reports medium
- Graceful Shutdown Executor Context Manager in Python medium
- How to Build a Producer-Consumer Pattern with asyncio.Queue in Python medium
- How to Cancel an asyncio Task with Graceful Cleanup in Python medium
- How to Convert Data in Parallel with ThreadPoolExecutor in Python easy
Keep learning
Related tutorials and quizzes for this topic.