Реализовать Promise.any с нуля
Реализуйте promiseAny(promises), повторяющую Promise.any: она выполняется значением первого входа, который выполнился, и отклоняется, только если отклонятся все входы. Требования: резолвить, как только любой завершится успешно; если все отклонятся, отклонить с AggregateError, чей массив errors хранит каждое отклонение в порядке входа; пустой вход отклоняется сразу с AggregateError.
function promiseAny(promises) {
// ваш код здесь
}
Допишите реализацию.
Верните new Promise. Ведите счётчик pending и массив errors размером со вход. Для каждого элемента: Promise.resolve(item).then(resolve, err => { errors[i] = err; if (--pending === 0) reject(new AggregateError(errors)); }). Первое выполнение побеждает.
- ✗Путать инверсию: резолвить на отклонении вместо выполнения
- ✗Отклонять на первом отклонении, а не ждать, пока отклонятся все
- ✗Забыть случай пустого входа, который должен отклониться с
AggregateError
- →Чем
Promise.anyотличается отPromise.raceпри отклонении? - →Почему пустой вход отклоняется, а не остаётся pending навсегда?
Решение
Первое выполнение побеждает; отклонение лишь когда все отклонятся.
function promiseAny(promises) {
return new Promise((resolve, reject) => {
const items = [...promises];
const errors = new Array(items.length);
let pending = items.length;
if (pending === 0) {
return reject(new AggregateError([], 'All promises were rejected')); // пустой вход
}
items.forEach((item, i) => {
Promise.resolve(item).then(resolve, (err) => { // первое выполнение → resolve
errors[i] = err; // копим ошибки по индексу
if (--pending === 0) { // все отклонились
reject(new AggregateError(errors, 'All promises were rejected'));
}
});
});
});
}
Как это работает
Promise.resolve(item) оборачивает не-промисы. Первый успех вызывает resolve и фиксирует внешний промис — последующие колбэки игнорируются. Каждое отклонение пишет ошибку по индексу и уменьшает pending; при нуле все входы отклонились, и мы отклоняем AggregateError со списком в порядке входа. Пустой вход сразу отклоняется.