> Что происходит при встрече промиса во время выполнения синхронного кода в JavaScript (JavaScript)

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

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

Стек: JavaScript

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

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

При встрече промиса в синхронном коде его executor (функция-исполнитель) выполняется синхронно, а колбэки .then(), .catch() и .finally() попадают в microtask queue. Они выполнятся после завершения текущего синхронного кода и до обработки следующего macrotask (например, setTimeout). Это гарантирует, что асинхронные операции не блокируют основной поток.

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

Promise в JavaScript - это объект, представляющий будущий результат асинхронной операции. Когда интерпретатор встречает new Promise(executor), он немедленно вызывает executor синхронно. Внутри executor может быть как синхронный код (например, resolve(42)), так и асинхронный (например, setTimeout(() => resolve(data), 0)).

После вызова resolve() или reject() промис переходит в состояние fulfilled или rejected. Колбэки, зарегистрированные через .then(), .catch() или .finally(), не выполняются сразу - они помещаются в microtask queue. Event loop обрабатывает microtask queue после завершения текущего синхронного стека и перед следующим macrotask.

Этот механизм критичен для понимания порядка выполнения: синхронный код всегда выполняется первым, затем microtasks (промисы, MutationObserver), затем macrotasks (setTimeout, setInterval, I/O). Если microtask порождает новый microtask, он будет обработан до перехода к macrotask.

На практике

Для senior-разработчика важно понимать, как промисы взаимодействуют с event loop для предотвращения race conditions и оптимизации производительности. Например, при работе с анимациями или пользовательским вводом нужно учитывать, что .then() выполнится только после завершения текущего фрейма рендеринга.

Типичный сценарий: загрузка данных с сервера. Промис позволяет не блокировать UI, но порядок обработки ответа зависит от microtask queue. Если в коде есть несколько промисов, их колбэки выполнятся в порядке регистрации, но после всего синхронного кода.

Также важно помнить, что async/await - это синтаксический сахар над промисами. await приостанавливает выполнение функции, но не блокирует поток - он передаёт управление обратно в event loop, а возобновление происходит через microtask.

Пример кода

JAVASCRIPT
console.log('1: sync start');
const promise = new Promise((resolve) => {
console.log('2: executor sync');
resolve('3: resolved value');
});
promise.then((value) => {
console.log(value);
});
console.log('4: sync end');
// Вывод:
// 1: sync start
// 2: executor sync
// 4: sync end
// 3: resolved value

Executor выполняется синхронно, .then() - асинхронно, после синхронного кода.

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

Начни с ключевого факта: executor выполняется синхронно, колбэки - через microtask queue. Затем объясни порядок обработки event loop: sync → microtasks → macrotasks. Приведи пример с setTimeout для контраста. Упомяни, что async/await использует тот же механизм. Если спросят про вложенные промисы, объясни, что каждый .then() создаёт новый microtask, и они обрабатываются последовательно.

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

Интервьюер проверяет понимание event loop, разницу между microtask и macrotask, а также способность предсказывать порядок выполнения кода. Для senior-уровня важно не просто знать теорию, но и уметь применять её для отладки сложных сценариев, например, race conditions в асинхронных цепочках.

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

  • Утверждение, что промис выполняется асинхронно целиком (на самом деле executor синхронен).
  • Путаница между microtask и macrotask (например, ожидание, что .then() выполнится после setTimeout).
  • Игнорирование того, что async/await - это тот же механизм промисов.
  • Предположение, что Promise.resolve() немедленно вызывает колбэк (на самом деле он попадает в microtask queue).

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

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