Как обработать ошибки всех промисов и вызвать reject с AggregateError в JavaScript?
Что проверяют
Знаете ли вы разницу между комбинаторами промисов и умеете ли собрать все ошибки, а не только первую. Обычно следом просят объяснить, чем Promise.all отличается от Promise.allSettled и Promise.any и отменяются ли остальные запросы после первой ошибки.
Как отвечать
Начните с того, почему Promise.all не подходит: он отклоняется на первой ошибке и отдаёт только её, остальные причины теряются. Сами промисы при этом продолжают выполняться, отмены нет, для неё нужен AbortController.
Рабочая схема такая:
Promise.allSettled(promises): никогда не отклоняется, возвращает массив объектов{ status, value }или{ status, reason };- фильтр: из результатов берут записи со
status === 'rejected'и собирают ихreasonв массив; AggregateError: если массив не пуст, бросаютnew AggregateError(errors, 'Часть запросов упала'). Внутри.thenилиasync-функцииthrowпревращается в reject возвращённого промиса;- обработка: снаружи ловят ошибку в
catch, проверяютe instanceof AggregateErrorи читают массивe.errors.
Стоит упомянуть Promise.any: он сам отклоняется с AggregateError, но только когда упали все промисы, а при первом успехе возвращает его значение. Это «первый удачный ответ», а не «собрать все ошибки». AggregateError и Promise.any появились в ES2021 и есть во всех актуальных браузерах и Node.js.
Для фронтенда полезно добавить практику: успешные значения не выбрасывайте. Их можно вернуть рядом с ошибкой, чтобы интерфейс показал то, что загрузилось, и отдельно сообщил про остальное.
Где спотыкаются
Пишут Promise.all с .catch на каждом промисе и не замечают, что ошибки превратились в undefined в массиве результатов, а общий промис стал успешным.
Ждут, что Promise.allSettled сам отклонится при ошибках. Он всегда выполняется успешно, решение о reject принимает ваш код.
Путают поля: у AggregateError список причин лежит в errors, а message это одна общая строка. Логируют только message и теряют сами ошибки.