Как обработать успешное разрешение одного из промисов в функции any?
Что проверяют
Понимаете ли вы разницу между четырьмя комбинаторами промисов и умеете ли обработать случай, когда успеха не было вовсе. Следом почти всегда спрашивают, чем Promise.any отличается от Promise.race и что происходит с остальными запросами после победы одного.
Как отвечать
Promise.any (ES2021) возвращает промис, который выполняется значением первого успешного промиса из переданного итерируемого. Отклонения не мешают, пока остаётся хоть один кандидат. Если отклонились все, итоговый промис отклоняется с AggregateError, у которого в поле errors лежат причины в порядке исходного массива. Пустой массив сразу даёт отклонение с AggregateError, а не вечное ожидание.
Обработка в стиле async/await:
- успех:
const data = await Promise.any(requests)отдаёт значение победителя; - общий провал: в
catchпроверяйтеe instanceof AggregateErrorи разбирайтеe.errors, а неe.message; - отмена проигравших: остальные промисы продолжают выполняться,
Promise.anyих не останавливает. Дляfetchдайте каждому запросу свойAbortControllerи после победы отмените остальные; - что считать успехом:
fetchне отклоняется на ответах 404 и 500, поэтому в каждой ветке проверяйтеres.okи бросайте ошибку сами, иначе победит первый же ответ с ошибкой.
Для сравнения: Promise.race берёт первый завершившийся, в том числе с ошибкой; Promise.all падает на первой ошибке и для успеха ждёт всех; Promise.allSettled не отклоняется никогда и возвращает статус каждого. Типичный сценарий для any: запрос к нескольким зеркалам или CDN, чтобы взять самый быстрый рабочий ответ.
Где спотыкаются
Путают any с race. Если первым упадёт один из запросов, race отклонится сразу, а any дождётся успешного.
Не отменяют проигравшие запросы. Трафик и нагрузка на сервер растут кратно, а если у запросов есть побочные эффекты (например, POST), выполнятся все.
Обрабатывают общий провал как обычную ошибку и логируют e.message, теряя причины каждого отказа, которые лежат в e.errors.