← Назад к вопросам

Реализовать Promise.all

2.0 Middle🔥 231 комментариев
#JavaScript Core#Архитектура и паттерны#Браузер и сетевые технологии

Условие

Напишите собственную реализацию метода Promise.all.

Требования

  1. Функция promiseAll(promises) должна принимать массив промисов

  2. Возвращать промис, который:

    • Резолвится массивом результатов, когда все промисы выполнены
    • Реджектится с ошибкой первого отклонённого промиса
    • Сохраняет порядок результатов соответственно входному массиву

Пример

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 же реджектится при первой ошибке, что делает его более строгим для критических операций.

Реализовать Promise.all | PrepBro