Реализовать Promise.all с нуля
Реализуйте promiseAll(promises), повторяющую Promise.all: она возвращает промис, выполняющийся массивом результатов в порядке входа или отклоняющийся, как только отклонится любой вход. Требования: результаты должны сохранять исходный индекс независимо от порядка завершения; пустой вход выполняется сразу с []; не-промисы допустимы и проходят как есть.
function promiseAll(promises) {
// ваш код здесь
}
Допишите реализацию.
Верните new Promise; если вход пуст, сразу resolve []. Ведите счётчик remaining и массив results. Для каждого элемента: Promise.resolve(item).then(value => { results[i] = value; if (--remaining === 0) resolve(results); }, reject). Запись по индексу сохраняет порядок несмотря на тайминг завершения, а первый reject разрешает внешний промис (поздние отклонения игнорируются).
- ✗Делать push результатов вместо записи по индексу, путая порядок при завершении вразнобой
- ✗Забыть случай пустого входа, который без раннего
[]никогда не разрешится - ✗Не оборачивать элементы в
Promise.resolve, из-за чего обычные не-промисы ломают.then
- →Как изменить это, чтобы реализовать
Promise.allSettledвместо текущего поведения? - →Зачем оборачивать каждый элемент в
Promise.resolveдля не-промис значений?
Решение
Счётчик оставшихся плюс запись по индексу сохраняют порядок; первый reject завершает всё.
function promiseAll(promises) {
return new Promise((resolve, reject) => {
const items = [...promises];
const results = new Array(items.length);
let remaining = items.length;
if (remaining === 0) return resolve(results); // пустой вход
items.forEach((item, i) => {
Promise.resolve(item).then((value) => { // оборачиваем не-промисы
results[i] = value; // запись по индексу = порядок
if (--remaining === 0) resolve(results);
}, reject); // первый reject завершает внешний промис
});
});
}
Как это работает
Каждый элемент оборачивается в Promise.resolve, поэтому обычные значения тоже получают .then. Результат пишется по индексу i, а не через push, — поэтому порядок выходного массива совпадает со входом, даже если промисы завершаются вразнобой. Счётчик remaining уменьшается на каждом успехе; при достижении нуля внешний промис резолвится. Любое отклонение сразу вызывает reject (последующие отклонения игнорируются, так как промис уже разрешён). Пустой вход резолвится [] немедленно.