Каким методом промиса получить результат первого выполненного запроса
Что проверяют
Различаете ли вы «первый завершившийся» и «первый успешный». Это два разных метода, Promise.race и Promise.any, и ответ «race» без оговорок интервьюер обычно добивает вопросом: а если первой придёт ошибка?
Как отвечать
Promise.race(promises): принимает исход первого завершившегося промиса, каким бы он ни был. Если первым отклонился запрос, отклонится иrace, даже если остальные через миг ответили бы успешно;Promise.any(promises)(ES2021): ждёт первый успешно выполненный. Отклонения пропускает, а если отклонены все, отклоняется сAggregateError, в полеerrorsкоторого лежат все причины.
Для «результата первого выполненного запроса» по смыслу подходит any: одни и те же данные запрашиваются с нескольких зеркал или CDN, и нужен любой рабочий ответ.
race хорош для гонки с таймером, классический таймаут:
const timeout = (ms) => new Promise((_, reject) =>
setTimeout(() => reject(new Error("timeout")), ms));
const data = await Promise.race([fetch(url), timeout(5000)]);Для fetch сейчас удобнее встроенный AbortSignal.timeout(5000), переданный в signal: он не только отклоняет промис, но и реально прерывает запрос.
Крайние случаи: Promise.race([]) навсегда остаётся в ожидании, а Promise.any([]) сразу отклоняется с AggregateError.
Где спотыкаются
Называют race методом «первого успешного» и не знают про any. Одна быстрая сетевая ошибка роняет весь сценарий, хотя соседний запрос вот-вот вернул бы данные.
Думают, что проигравшие промисы отменяются. Запросы продолжают идти и тратить трафик, а их обработчики then всё равно выполнятся и могут перезаписать состояние устаревшими данными. Прерывайте их через AbortController, как только есть победитель.
Ловят AggregateError как обычную ошибку и показывают только общее сообщение. Реальные причины отказа лежат в массиве errors, и без них непонятно, что сломалось: сеть, CORS или ответ сервера.