Python async IO has grown up. The gather-and-pray era is over. Python 3.11+ introduced TaskGroup and asyncio.timeout, and 3.14 adds eager task starts, queue shutdown APIs, and finer runner control. If you're still writing try/except around gather(), you're working too hard and hiding bugs.
Structured concurrency is the new default
TaskGroup replaces gather() for nearly every use case. It guarantees that if one child fails, the others are cancelled automatically. No more orphaned tasks leaking memory or holding connections open.
Timeouts that actually work
asyncio.timeout() (3.11+) and timeout_at() are context managers, not decorator hacks. They cancel the enclosed block cleanly on expiry.
Eager task starts in 3.14
Python 3.14 adds TaskGroup.create_task(eager_start=True). The coroutine runs synchronously until its first await, reducing scheduler overhead for tasks that often complete immediately (cache hits, fast paths).
Queues with shutdown semantics
asyncio.Queue now supports shutdown() and shutted_down(). Producers call shutdown(); consumers detect it via queue.get() raising QueueShutDown. No more sentinel values or custom protocols.
Runner control for apps and tests
asyncio.Runner (3.11+) lets you reuse a loop with a custom context. In 3.14, runner.run() accepts a timeout and returns the loop for inspection. Great for test fixtures and embedding.
Common pitfalls still worth avoiding
| Anti-pattern | Fix |
|---|---|
| await gather(*tasks) without try/except | Use TaskGroup |
| Creating tasks but not awaiting them | Track in TaskGroup or list |
| Blocking calls in async functions | Run in executor: await loop.run_in_executor(None, blocking_fn) |
| Ignoring CancelledError | Let it propagate; cleanup in finally |
Migration checklist
1. Replace gather() with TaskGroup. 2. Wrap network boundaries with asyncio.timeout(). 3. Use eager_start for cache-heavy workloads. 4. Replace sentinel queues with queue.shutdown(). 5. Adopt Runner for integration tests. 6. Run your test suite with PYTHONASYNCIODEBUG=1 to catch unawaited coroutines.
"Structured concurrency isn't syntax sugar. It's the difference between a server that recovers from overload and one that leaks until OOM.
— Guido van Rossum (paraphrased)
✦








