DEV Community

TildAlice
TildAlice

Posted on • Originally published at tildalice.io

asyncio.gather vs as_completed vs TaskGroup: 3 Patterns

Why gather() Silently Ate My Exception and Delayed Everything Else

I had a scraper hitting 50 API endpoints concurrently. One endpoint started returning 500s. The entire batch hung for the full timeout window before failing — even though 49 endpoints succeeded in under 2 seconds.

The problem? I used asyncio.gather() without return_exceptions=True. The failing task raised an exception that bubbled up immediately, canceling all other tasks. But because I didn't catch it properly, the event loop waited for cleanup. The symptom looked like a hang.

Switching to asyncio.as_completed() fixed it — successful responses came back immediately, and I handled failures as they occurred. But that wasn't the end of the story. When I migrated to Python 3.11, TaskGroup gave me structured concurrency and automatic cleanup that neither of the old patterns provided.

Here's what I learned about when each pattern actually makes sense.

A person reads 'Python for Unix and Linux System Administration' indoors.

Photo by Christina Morillo on Pexels

gather(): Fast When Everything Succeeds, Fragile When Anything Fails

The signature looks simple:


python

---

*Continue reading the full article on [TildAlice](https://tildalice.io/asyncio-gather-as-completed-taskgroup-patterns/)*
Enter fullscreen mode Exit fullscreen mode

Top comments (0)