Что происходит с остальными задачами, если одна задача в asyncio.gather завершилась исключением в Python
Что проверяют
Знаете ли вы, что asyncio.gather не отменяет соседние задачи. Это тот случай, когда очевидный ответ неверен, и по нему хорошо видно, писал человек асинхронный код или читал про него.
Как отвечать
По умолчанию первое исключение пробрасывается вызывающему коду сразу, не дожидаясь остальных. Остальные задачи при этом продолжают выполняться: их никто не отменяет, их результаты просто теряются.
- если такая задача упадёт позже, забрать исключение будет некому, и в лог прилетит предупреждение о необработанном исключении задачи;
return_exceptions=Trueменяет поведение: ожидание идёт до конца, а результат это список, где на месте упавших лежат объекты исключений в том же порядке, что и аргументы;- если отменяют сам вызов
gather, вот тогда отменяются и все незавершённые задачи внутри; - когда нужно поведение «упал один, гасим всех», берут группу задач
asyncio.TaskGroup: она отменяет соседей и отдаёт группу исключений.
results = await asyncio.gather(*coros, return_exceptions=True)
for coro, res in zip(coros, results):
if isinstance(res, BaseException):
log.warning("не выполнено: %r", res)Где спотыкаются
Уверены, что ошибка в одной задаче останавливает остальные. Вызывающий код уже ушёл в обработчик ошибки, а брошенные задачи продолжают ходить в сеть и писать в базу. Отсюда записи, появившиеся после якобы отменённой операции.
Ставят return_exceptions=True и оставляют вокруг вызова перехват исключения. Исключение там больше не поднимается, проверять нужно типы элементов списка, иначе объект ошибки поедет дальше по коду как обычный результат.
Забывают, что в этом режиме в список попадает и отмена. Корутина, которую попросили остановиться, вернётся объектом CancelledError, и если проверка смотрит только на Exception, отмена будет принята за успешное выполнение.