> Реализовать функцию, принимающую массив промисов и возвращающую массив результатов или первый reject в JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: IT-One

Стек: JavaScript

> Пример ответа

Короткий ответ

Реализация promiseAll сводится к созданию массива результатов с сохранением порядка, параллельному запуску всех промисов и reject при первой ошибке. Для этого используем Promise.all или пишем свою версию через then и catch, отслеживая количество завершённых промисов. Важно обрабатывать пустой массив и не-промис значения.

Подробное объяснение

Функция promiseAll принимает iterable промисов и возвращает новый Promise. Основные требования:

  • Сохраняет порядок результатов согласно исходному массиву
  • Возвращает массив результатов при успешном завершении всех промисов
  • Отклоняет результат при первом же reject с ошибкой этого промиса
  • Работает с пустым массивом (возвращает resolved с пустым массивом)
  • Обрабатывает не-промис значения (оборачивает их в Promise.resolve)

Реализация использует счётчик завершённых промисов и массив результатов с индексами. Каждый промис при resolve сохраняет результат в соответствующую позицию и увеличивает счётчик. Когда счётчик равен длине массива - resolve с результатами. При любом reject - сразу reject с ошибкой.

На практике

В реальных проектах используют встроенный Promise.all, но понимание его реализации важно для:

  • Отладки асинхронного кода
  • Создания кастомных вариаций (например, с таймаутом или retry)
  • Работы в окружениях без поддержки современных API
  • Оптимизации под специфичные сценарии (например, лимит параллельных запросов)

Пример кода

JAVASCRIPT
function promiseAll(promises) {
return new Promise((resolve, reject) => {
if (!promises || typeof promises[Symbol.iterator] !== 'function') {
reject(new TypeError('Argument must be iterable'));
return;
}
const arr = Array.from(promises);
if (arr.length === 0) {
resolve([]);
return;
}
const results = new Array(arr.length);
let completed = 0;
arr.forEach((item, index) => {
Promise.resolve(item).then(
(value) => {
results[index] = value;
completed++;
if (completed === arr.length) {
resolve(results);
}
},
(error) => {
reject(error);
}
);
});
});
}

Как отвечать на собеседовании

Начни с описания сигнатуры и поведения функции. Объясни ключевые моменты: сохранение порядка, параллельное выполнение, обработка ошибок. Покажи реализацию и прокомментируй важные детали: использование Promise.resolve для не-промисов, проверку на iterable, работу с пустым массивом. Обсуди trade-off: почему нельзя использовать forEach с async/await (последовательное выполнение) и как избежать race condition при записи результатов.

Что проверяет интервьюер

  • Понимание асинхронности и промисов
  • Умение работать с коллбеками и замыканиями
  • Внимание к edge cases (пустой массив, не-промис значения, ошибки)
  • Способность реализовать параллельное выполнение без гонок данных
  • Знание встроенных методов Promise и их отличий

Типичные ошибки

  • Использование forEach с async/await - приводит к последовательному выполнению
  • Неправильная обработка пустого массива (возврат pending промиса)
  • Отсутствие обёртки не-промисов через Promise.resolve
  • Использование results.push() вместо индексов - нарушает порядок
  • Необработка случая, когда передан не iterable объект
  • Утечка памяти при большом количестве промисов (не критично для собеседования)

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью