← Назад к вопросам
Реализовать Promise.all
2.0 Middle🔥 231 комментариев
#JavaScript Core#Архитектура и паттерны#Браузер и сетевые технологии
Условие
Напишите собственную реализацию метода Promise.all.
Требования
-
Функция promiseAll(promises) должна принимать массив промисов
-
Возвращать промис, который:
- Резолвится массивом результатов, когда все промисы выполнены
- Реджектится с ошибкой первого отклонённого промиса
- Сохраняет порядок результатов соответственно входному массиву
Пример
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = Promise.resolve(3);
promiseAll([p1, p2, p3]).then(results => {
console.log(results); // [1, 2, 3]
});
// С ошибкой
const p4 = Promise.reject(new Error("Failed"));
promiseAll([p1, p4, p3]).catch(err => {
console.log(err.message); // "Failed"
});
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Реализация Promise.all
Понимание задачи
Promise.all — это фундаментальный метод для работы с несколькими асинхронными операциями одновременно. Он ждёт выполнения всех промисов и возвращает единственный промис с массивом результатов в исходном порядке. Это критически важно для сценариев, где нужны результаты нескольких асинхронных вызовов перед дальнейшей обработкой.
Базовая реализация
function promiseAll(promises) {
return new Promise((resolve, reject) => {
// Обработка пустого массива
if (promises.length === 0) {
resolve([]);
return;
}
const results = new Array(promises.length);
let completedCount = 0;
promises.forEach((promise, index) => {
// Преобразуем значения в промисы
Promise.resolve(promise).then(
(value) => {
results[index] = value;
completedCount++;
// Резолвим, когда все промисы выполнены
if (completedCount === promises.length) {
resolve(results);
}
},
(error) => {
// Реджектим с первой ошибкой
reject(error);
}
);
});
});
}
Версия с обработкой не-промисов
function promiseAll(promises) {
return new Promise((resolve, reject) => {
// Обработка пустого массива
if (!Array.isArray(promises) || promises.length === 0) {
resolve([]);
return;
}
const results = new Array(promises.length);
let completedCount = 0;
let hasRejected = false;
promises.forEach((promise, index) => {
// Обработка значений, которые не являются промисами
if (promise && typeof promise.then === "function") {
promise.then(
(value) => {
if (hasRejected) return;
results[index] = value;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
},
(error) => {
if (!hasRejected) {
hasRejected = true;
reject(error);
}
}
);
} else {
// Не-промис обрабатываем как уже выполненное значение
results[index] = promise;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
}
});
});
}
TypeScript версия
function promiseAll<T>(
promises: Promise<T>[] | T[]
): Promise<T[]> {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises) || promises.length === 0) {
resolve([]);
return;
}
const results: T[] = new Array(promises.length);
let completedCount = 0;
let hasRejected = false;
promises.forEach((promise, index) => {
Promise.resolve(promise).then(
(value: T) => {
if (hasRejected) return;
results[index] = value;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
},
(error: Error) => {
if (!hasRejected) {
hasRejected = true;
reject(error);
}
}
);
});
});
}
Примеры использования
// Базовый пример
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = Promise.resolve(3);
promiseAll([p1, p2, p3]).then(results => {
console.log(results); // [1, 2, 3]
});
// С не-промисами
promiseAll([1, Promise.resolve(2), 3]).then(results => {
console.log(results); // [1, 2, 3]
});
// С ошибкой
const p4 = Promise.reject(new Error("Failed"));
promiseAll([p1, p4, p3]).catch(err => {
console.log(err.message); // "Failed"
});
// С пустым массивом
promiseAll([]).then(results => {
console.log(results); // []
});
// API запросы
const fetchUsers = fetch("/api/users").then(r => r.json());
const fetchPosts = fetch("/api/posts").then(r => r.json());
const fetchComments = fetch("/api/comments").then(r => r.json());
promiseAll([fetchUsers, fetchPosts, fetchComments]).then(
([users, posts, comments]) => {
console.log(users, posts, comments);
}
).catch(error => {
console.error("Ошибка загрузки данных", error);
});
Альтернативный подход с async/await
async function promiseAll(promises) {
try {
const results = await Promise.all(promises);
return results;
} catch (error) {
throw error;
}
}
Ключевые моменты реализации
- Promise.resolve(): преобразует любое значение в промис для унификации
- Порядок результатов: используем индекс для сохранения исходного порядка
- Счётчик завершённых: отслеживаем количество выполненных промисов
- Ранний реджект: останавливаем обработку при первой ошибке
- Флаг hasRejected: предотвращаем множественные реджекты
- Пустой массив: обрабатываем как edge case для резолва
Различие от Promise.allSettled()
Promise.allSettled() ждёт выполнения всех промисов (успех или ошибка) и возвращает массив объектов с status и value/reason. Promise.all же реджектится при первой ошибке, что делает его более строгим для критических операций.