> Как создать промис, который возвращает результат первого успешно завершившегося промиса в JavaScript (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: Яндекс
Стек: JavaScript
> Пример ответа
Короткий ответ
Для создания промиса, возвращающего результат первого успешно завершившегося промиса, используйте Promise.any(). Он принимает итерируемую коллекцию промисов и разрешается первым выполненным (fulfilled). Если все промисы отклонены, Promise.any() отклоняется с AggregateError. Альтернатива - Promise.race() с дополнительной логикой, но Promise.any() - стандартное и предпочтительное решение.
Подробное объяснение
Promise.any() - это статический метод, добавленный в ES2021. Он работает следующим образом:
- Принимает итерируемый объект (например, массив) промисов.
- Возвращает новый промис, который разрешается значением первого промиса, перешедшего в состояние
fulfilled. - Если все промисы отклонены (
rejected), возвращаемый промис отклоняется сAggregateError, содержащим все причины отклонений.
Отличие от Promise.race(): Promise.race() разрешается первым завершившимся промисом (независимо от результата - успех или ошибка). Promise.any() игнорирует отклонённые промисы и ждёт первый успешный.
Для имитации Promise.any() в старых средах можно реализовать вручную: создать массив для ошибок, счётчик отклонений и разрешать промис при первом успехе.
На практике
Promise.any() полезен в сценариях, где нужен быстрый успешный ответ от нескольких источников:
- Загрузка ресурсов из нескольких CDN: берём первый успешно загруженный.
- Запросы к нескольким API-эндпоинтам с одинаковыми данными (redundancy).
- Выбор самого быстрого сервера из пула.
Важно помнить: если все промисы отклонены, обработка ошибки через catch обязательна, иначе возникнет необработанное отклонение.
Пример кода
JAVASCRIPT// Базовое использование Promise.any()const promise1 = new Promise((resolve, reject) =>setTimeout(reject, 100, 'Ошибка 1'));const promise2 = new Promise((resolve, reject) =>setTimeout(resolve, 200, 'Успех 2'));const promise3 = new Promise((resolve, reject) =>setTimeout(resolve, 300, 'Успех 3'));Promise.any([promise1, promise2, promise3]).then(result => console.log(result)) // 'Успех 2'.catch(error => console.error(error));// Обработка случая, когда все промисы отклоненыconst allRejected = [Promise.reject('Ошибка A'),Promise.reject('Ошибка B')];Promise.any(allRejected).catch(error => {console.log(error instanceof AggregateError); // trueconsole.log(error.errors); // ['Ошибка A', 'Ошибка B']});// Ручная реализация для старых сред (без Promise.any)function promiseAny(promises) {return new Promise((resolve, reject) => {const errors = [];let rejectedCount = 0;const total = promises.length;if (total === 0) {reject(new AggregateError([], 'All promises were rejected'));return;}promises.forEach((promise, index) => {Promise.resolve(promise).then(resolve).catch(error => {errors[index] = error;rejectedCount++;if (rejectedCount === total) {reject(new AggregateError(errors, 'All promises were rejected'));}});});});}
Как отвечать на собеседовании
Начните с прямого ответа: используйте Promise.any(). Затем объясните разницу с Promise.race() и Promise.allSettled(). Приведите пример из практики, где Promise.any() решает реальную задачу (например, fallback для CDN). Упомяните обработку AggregateError и совместимость (ES2021). Если спросят про альтернативы, покажите ручную реализацию. Не углубляйтесь в микродетали event loop, если не спросят.
Что проверяет интервьюер
- Знание современных возможностей JavaScript (ES2021+).
- Понимание различий между методами
Promise(all,race,any,allSettled). - Умение обрабатывать краевые случаи (пустой массив, все промисы отклонены).
- Практический опыт: когда и зачем использовать
Promise.any(). - Способность реализовать аналог вручную (проверка глубокого понимания).
Типичные ошибки
- Путают
Promise.any()сPromise.race()- не учитывают, чтоraceреагирует на первый отклонённый промис. - Забывают обработать случай, когда все промисы отклонены (нет
catch). - Не учитывают, что
Promise.any()с пустым массивом сразу отклоняется сAggregateError. - Пытаются использовать
Promise.all()илиPromise.allSettled()для этой задачи. - Не знают про
AggregateErrorи его свойствоerrors.
> Похожие задачи по frontend
В чем разница между var, const и let в JavaScript
Как распределялось время между фронтендом и бэкендом в работе
Каким методом промиса выполнить несколько параллельных запросов и получить результат каждого
Какой опыт есть с особенностями браузеров
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью