> Как повесить обработчик на резолв каждого промиса в массиве для реализации any? (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Яндекс

Стек: JavaScript

> Пример ответа

Короткий ответ

Для реализации Promise.any не нужно вешать обработчик на резолв каждого промиса в массиве. Достаточно создать массив промисов и передать его в Promise.any. Если нужно отслеживать резолв каждого промиса, можно использовать Promise.allSettled или обернуть каждый промис в дополнительный .then() с колбэком. Но для Promise.any это избыточно - он сам возвращает первый успешно завершённый промис.

Подробное объяснение

Promise.any - это встроенный метод, который принимает итерируемый объект промисов и возвращает один промис, который резолвится значением первого успешно завершённого промиса. Если все промисы отклонены, возвращается AggregateError. Для кастомной реализации Promise.any можно использовать подход с отслеживанием состояния каждого промиса через обработчик на .then(). Это нужно, чтобы определить, какой промис завершился первым успешно, и зарезолвить итоговый промис. Обработчик вешается на каждый промис через .then(value => resolve(value)), где resolve - это функция резолва внешнего промиса. При этом важно обработать отклонения: для каждого промиса добавляется .catch(), который увеличивает счётчик ошибок. Когда счётчик достигает длины массива, внешний промис отклоняется с AggregateError.

На практике

В реальных проектах не нужно писать кастомный Promise.any - используйте встроенный. Но если интервьюер просит реализовать его вручную, важно показать понимание работы с промисами и обработки ошибок. Ключевые моменты: создание внешнего промиса, итерация по массиву, вызов .then() на каждом элементе с вызовом resolve, и .catch() для подсчёта ошибок. Также нужно учесть случай пустого массива - сразу отклонить с AggregateError. Для проверки завершения всех промисов используйте счётчик, а не Promise.all, так как Promise.all отклоняется при первой ошибке.

Пример кода

JAVASCRIPT
function promiseAny(promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
reject(new AggregateError([], 'All promises were rejected'));
return;
}
let rejectedCount = 0;
const errors = new Array(promises.length);
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(value => {
resolve(value);
})
.catch(error => {
errors[index] = error;
rejectedCount++;
if (rejectedCount === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
});
});
});
}

Как отвечать на собеседовании

Начни с краткого объяснения, что Promise.any уже есть в стандарте, но если нужно реализовать вручную, то ключевой момент - повесить обработчик на .then() каждого промиса, который вызывает resolve внешнего промиса. Подчеркни, что обработчик должен быть именно на резолв, а не на reject. Объясни, как обрабатываются ошибки через счётчик и AggregateError. Упомяни edge cases: пустой массив, не-промис значения (используй Promise.resolve), и что метод должен работать с любым итерируемым объектом. Не углубляйся в микрооптимизации, пока не спросят.

Что проверяет интервьюер

Интервьюер проверяет понимание работы промисов, умение создавать кастомные асинхронные утилиты, знание AggregateError и обработку ошибок. Также оценивается способность рассмотреть edge cases и написать чистый, рабочий код без лишних зависимостей. Важно показать, что ты понимаешь разницу между Promise.any, Promise.race, Promise.all и Promise.allSettled.

Типичные ошибки

  • Использование Promise.all для отслеживания завершения всех промисов - он отклоняется при первой ошибке, что не подходит для Promise.any.
  • Забывание обернуть не-промис значения в Promise.resolve.
  • Неправильная обработка пустого массива - нужно сразу отклонить промис.
  • Использование reject вместо resolve при первом успешном завершении.
  • Неучёт того, что AggregateError должен содержать массив ошибок в том же порядке, что и исходные промисы.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью