Как повесить обработчик на резолв каждого промиса в массиве для реализации any?
Что проверяют
Понимаете ли вы семантику Promise.any и умеете ли собрать её руками из then. Интервьюер смотрит на граничные случаи: пустой массив, значения, которые не являются промисами, порядок ошибок. Следом обычно просят сравнить с Promise.race и Promise.allSettled.
Как отвечать
Promise.any (ES2021) выполняется с первым успешным значением и отклоняется, только если отклонились все, причём ошибкой AggregateError с массивом причин в поле errors. Отсюда реализация: на каждый элемент вешаем then с двумя обработчиками. Успех сразу вызывает resolve, ошибка записывается по индексу и увеличивает счётчик.
function promiseAny(iterable) {
return new Promise((resolve, reject) => {
const items = Array.from(iterable);
const errors = [];
let rejected = 0;
if (items.length === 0) {
return reject(new AggregateError([], "All promises were rejected"));
}
items.forEach((item, i) => {
Promise.resolve(item).then(resolve, (err) => {
errors[i] = err;
if (++rejected === items.length) {
reject(new AggregateError(errors, "All promises were rejected"));
}
});
});
});
}Что стоит проговорить:
Promise.resolve(item): оборачивает обычные значения и thenable-объекты, иначе42в массиве сломает вызовthen;- повторный
resolve: игнорируется, промис меняет состояние один раз, поэтому остальные можно не отменять; - отдельный счётчик:
errors.lengthдля проверки не годится, запись по индексу 5 сразу делает длину 6; - пустой вход: сразу отклонение с пустым
AggregateError, как у встроенного метода.
Где спотыкаются
Берут Promise.allSettled и фильтруют результат. Он ждёт завершения всех промисов, а any должен ответить на первом успехе.
Путают any с race: race завершается первым исходом, в том числе ошибкой.
Складывают ошибки через push. Порядок причин тогда соответствует времени отказа, а не позициям входного массива.