> Расскажите про асинхронность в JavaScript и средства работы с асинхронным кодом (JavaScript)

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

Компании: amoCRM, циан, ООО Премиум ИТ Солюшен, Spotware, Домклик, EPAM, ООО Рокет Тех, Rutube, TYMY, Purrweb, ООО Снэп АйТи

Стек: JavaScript

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

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

Асинхронность в JavaScript - это способность выполнять операции без блокировки основного потока, основанная на event loop. Основные средства работы: callbacks, Promises и async/await. Event loop управляет очередями microtask и macrotask, обеспечивая неблокирующее выполнение. Для сложных сценариев используются комбинации Promise.all, Promise.race и обработка ошибок через try/catch.

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

JavaScript является однопоточным языком с одним call stack и одним heap. Асинхронность реализуется через event loop, который координирует выполнение синхронного кода, microtask (Promise.then/catch/finally, queueMicrotask) и macrotask (setTimeout, setInterval, I/O).

Callbacks - исторически первый механизм, но приводят к callback hell. Promises (ES2015) предоставляют цепочки .then() и .catch() для композиции асинхронных операций. Async/await (ES2017) - синтаксический сахар над Promises, позволяющий писать асинхронный код в синхронном стиле.

Важные концепции: Promise.all для параллельного выполнения, Promise.race для первого завершённого, Promise.allSettled для всех результатов независимо от ошибок. Обработка ошибок в async/await через try/catch обязательна, так как необработанные rejection приводят к unhandledrejection.

На практике

Для параллельных запросов к API используйте Promise.all с обработкой ошибок через .catch на каждом запросе или общий try/catch. Для последовательных операций - async/await с циклом for...of, а не forEach, так как последний не поддерживает await корректно.

Избегайте создания Promise вручную (new Promise) без необходимости - используйте существующие асинхронные функции. Для таймаутов и retry-логики применяйте Promise.race с setTimeout. В Node.js для работы с потоками используйте streams или worker_threads, если требуется CPU-интенсивная работа.

Пример кода

JAVASCRIPT
// Параллельная загрузка с обработкой ошибок
async function fetchUsers(ids) {
const promises = ids.map(id =>
fetch(`/api/users/${id}`)
.then(res => res.json())
.catch(err => ({ id, error: err.message }))
);
return Promise.all(promises);
}
// Последовательная обработка с таймаутом
async function processWithTimeout(items, timeout = 5000) {
const results = [];
for (const item of items) {
const result = await Promise.race([
processItem(item),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
results.push(result);
}
return results;
}

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

Начните с event loop как фундамента, затем перечислите средства: callbacks, Promises, async/await. Объясните разницу между microtask и macrotask, приведите примеры. Покажите понимание Promise combinators (all, race, allSettled, any). Упомяните обработку ошибок и антипаттерны (например, забытый return в .then). Для senior уровня добавьте про memory leaks при неотменённых таймерах или подписках, про отличие между асинхронностью и параллелизмом.

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

  • Понимание event loop и порядка выполнения (microtask vs macrotask)
  • Умение выбирать правильный инструмент под задачу (callbacks, Promises, async/await)
  • Знание Promise combinators и их особенностей
  • Навыки обработки ошибок в асинхронном коде
  • Понимание проблем (callback hell, unhandled rejection, race conditions)
  • Способность объяснить, как работает await под капотом (генераторы + Promise)

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

  • Забывают, что forEach не ждёт await - нужно использовать for...of или Promise.all
  • Не обрабатывают rejection в Promise.all - один reject убивает весь результат
  • Создают Promise вручную, когда достаточно существующего API
  • Путают параллельное и последовательное выполнение
  • Не отменяют таймеры или подписки при размонтировании компонента
  • Используют async/await без try/catch, полагаясь на глобальный обработчик
  • Думают, что async/await делает код многопоточным

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

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