Каким методом промиса получить результат первого выполненного запроса
Что проверяют
Для специалиста по данным вопрос про промисы обычно всплывает в сервисах вокруг модели: шлюз на Node.js к инференсу, запросы к нескольким LLM-провайдерам, фронт с TensorFlow.js. Проверяют, различаете ли вы «первый завершившийся» и «первый успешный», а следом часто просят показать то же самое на Python с asyncio.
Как отвечать
В JavaScript два метода:
Promise.race: берёт исход первого завершившегося промиса, включая ошибку. Удобен для таймаута, то есть гонки запроса с таймером;Promise.any(ES2021): ждёт первый успешный ответ и пропускает отклонения. Если упали все, отклоняется сAggregateError, где вerrorsлежат все причины.
Для «результата первого выполненного запроса» по смыслу нужен any. Типичный сценарий в ML-сервисе это хеджирование: один и тот же запрос уходит к двум репликам модели или двум провайдерам, берётся самый быстрый успешный ответ, остальные отменяются через AbortController.
Аналоги на Python:
asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED): возвращает множестваdoneиpending. Это аналогrace: задача, упавшая с исключением, тоже считается завершённой;asyncio.as_completed(tasks): отдаёт результаты по мере готовности, упавшие можно пропускать до первого успешного, это аналогany;asyncio.timeout()(Python 3.11+) илиasyncio.wait_for: таймаут без ручной гонки с таймером.
Получив результат, отмените оставшиеся задачи из pending через task.cancel(), иначе они продолжат работать и держать соединения.
Где спотыкаются
Отвечают «race» и не учитывают, что быстрая ошибка, например 429 от перегруженного провайдера, выиграет гонку и уронит весь запрос.
Не отменяют проигравшие запросы. При хеджировании это двойной счёт за токены и лишняя нагрузка на GPU, которая съедает весь выигрыш.
Путают конкурентность с параллелизмом. И промисы, и asyncio работают в одном потоке и ускоряют ожидание сети, а не тяжёлые вычисления: препроцессинг или инференс на CPU прямо в цикле событий заблокирует все остальные запросы.