> Как записывать ошибки в массив по индексу, соответствующему индексу промиса в JavaScript? (JavaScript)

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

Компании: Яндекс

Стек: JavaScript

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

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

Для записи ошибок в массив по индексу, соответствующему индексу промиса, используйте Promise.allSettled или Promise.all с обработкой каждого промиса через .catch. В Promise.allSettled каждый результат содержит status и value или reason, что позволяет сопоставить ошибку с исходным индексом. Альтернативно - маппинг промисов с сохранением индекса через Promise.all и массив ошибок фиксированной длины.

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

Основная задача - сохранить соответствие между порядком промисов в исходном массиве и ошибками, которые могут возникнуть при их выполнении. Это важно для асинхронных операций, где нужно точно знать, какой именно промис упал.

Promise.allSettled - наиболее прямой путь: он возвращает массив объектов с полями status ("fulfilled" или "rejected"), value и reason. Индекс объекта в результирующем массиве совпадает с индексом исходного промиса. Затем можно пройтись по результатам и собрать ошибки в массив по тем же индексам.

Promise.all не подходит напрямую, так как при первом rejection он сразу переходит в catch, теряя информацию об остальных промисах. Однако можно обернуть каждый промис в .catch, который возвращает объект ошибки, и тогда Promise.all дождётся всех. При этом нужно сохранить индекс - например, через Promise.all(promises.map((p, i) => p.catch(err => ({ index: i, error: err })))).

Для точного соответствия индексов удобно заранее создать массив ошибок фиксированной длины, заполненный null, и заполнять его по индексу при обработке каждого промиса.

На практике

В реальных проектах часто нужно загрузить несколько ресурсов параллельно и понять, какой именно не загрузился. Например, при загрузке изображений или данных для разных виджетов на странице. Использование Promise.allSettled даёт полную картину без потери информации.

Если требуется только массив ошибок, а успешные результаты не нужны, можно комбинировать Promise.allSettled с фильтрацией. Для критичных операций, где важен порядок, лучше явно сохранять индекс при обработке.

Пример кода

JAVASCRIPT
// Исходный массив промисов
const promises = [
fetch('/api/data1'),
fetch('/api/data2'),
fetch('/api/data3')
];
// Вариант 1: Promise.allSettled
async function collectErrorsWithAllSettled(promises) {
const results = await Promise.allSettled(promises);
const errors = new Array(promises.length).fill(null);
results.forEach((result, index) => {
if (result.status === 'rejected') {
errors[index] = result.reason;
}
});
return errors; // [null, Error, null] - ошибка только у второго промиса
}
// Вариант 2: Promise.all с сохранением индекса
async function collectErrorsWithAll(promises) {
const errors = new Array(promises.length).fill(null);
const wrapped = promises.map((p, i) =>
p.catch(err => {
errors[i] = err;
return undefined; // возвращаем что-то, чтобы Promise.all не упал
})
);
await Promise.all(wrapped);
return errors;
}

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

Начните с прямого ответа: используйте Promise.allSettled. Затем объясните, почему это предпочтительнее Promise.all - из-за обработки всех результатов, а не только первого rejection. Упомяните, что для точного соответствия индексов можно заранее создать массив ошибок. Если спросят про производительность, отметьте, что Promise.allSettled незначительно медленнее из-за дополнительной обёртки, но это оправдано надёжностью. Приведите пример из реальной практики, например, параллельная загрузка данных для дашборда.

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

Интервьюер оценивает понимание асинхронности в JavaScript, знание методов работы с промисами (Promise.all, Promise.allSettled, Promise.race), умение обрабатывать ошибки без потери контекста. Также проверяется способность проектировать надёжные асинхронные операции, где важна целостность данных. Senior-уровень подразумевает не только знание API, но и понимание trade-off между разными подходами.

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

  • Использование Promise.all без обёртки .catch - теряются все ошибки кроме первой.
  • Путаница между Promise.allSettled и Promise.all - первый ждёт все промисы, второй падает при первом rejection.
  • Игнорирование необходимости сохранять индекс - например, просто пуш в массив ошибок без привязки к порядку.
  • Забывание про null в массиве для успешных промисов - массив ошибок должен иметь ту же длину, что и исходный массив промисов.
  • Неправильная обработка вложенных асинхронных операций - если внутри промиса есть ещё асинхронность, нужно убедиться, что ошибка пробрасывается корректно.

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

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