> В какой момент вызывается callback в конструкторе Promise в JavaScript (JavaScript)

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

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

Стек: JavaScript

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

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

Callback в конструкторе Promise (executor) вызывается синхронно, немедленно при создании промиса, до того как конструктор вернет объект Promise. Это позволяет инициировать асинхронную операцию сразу. Сам callback получает два аргумента - resolve и reject, которые также являются функциями. Вызов resolve или reject может быть как синхронным, так и отложенным.

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

Конструктор Promise принимает функцию-executor с сигнатурой (resolve, reject) => void. Эта функция вызывается синхронно в момент вызова new Promise(executor). Это поведение строго определено спецификацией ECMAScript: executor запускается до того, как конструктор вернет созданный объект Promise.

Это означает, что код внутри executor выполняется в текущем стеке вызовов, до возврата управления вызывающему коду. Например:

JAVASCRIPT
console.log('before');
new Promise(() => console.log('inside executor'));
console.log('after');
// Вывод: before, inside executor, after

При этом resolve и reject - это функции, которые также создаются синхронно и передаются в executor. Их вызов может быть как синхронным, так и асинхронным. Если resolve/reject вызываются синхронно внутри executor, промис переходит в соответствующее состояние (fulfilled/rejected) немедленно, но обработчики .then()/.catch() все равно выполняются асинхронно (в микротасках).

На практике

Синхронный вызов executor важен для:

  • Немедленной валидации аргументов или инициализации
  • Возможности обернуть синхронный код в промис (например, Promise.resolve())
  • Гарантии, что побочные эффекты (логирование, подписки) происходят до возврата промиса

Однако стоит избегать тяжелых синхронных операций внутри executor, так как они блокируют event loop. Типичный паттерн - инициировать асинхронную операцию (setTimeout, fetch, addEventListener) и сохранить resolve/reject для вызова позже.

Пример кода

JAVASCRIPT
// Синхронный вызов executor
const promise = new Promise((resolve) => {
console.log('executor start');
resolve(42);
console.log('executor end');
});
console.log('after constructor');
// Вывод: executor start, executor end, after constructor
promise.then(value => console.log('then:', value));
// Вывод: then: 42 (в микротаске, после синхронного кода)

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

Начни с ключевого факта: executor вызывается синхронно, немедленно. Подчеркни, что это отличает его от callback в .then()/.catch(), которые всегда асинхронны. Приведи простой пример с порядком вывода. Упомяни, что resolve/reject могут вызываться как синхронно, так и асинхронно, но обработчики всегда выполняются в микротасках. Если спросят про спецификацию, упомяни, что это описано в разделе 25.6.3.1 Promise (executor).

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

  • Понимание разницы между синхронным и асинхронным выполнением в JavaScript
  • Знание event loop и очереди микротасков
  • Умение предсказывать порядок выполнения кода с промисами
  • Понимание внутреннего устройства конструктора Promise

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

  • Утверждение, что executor вызывается асинхронно (как setTimeout)
  • Путаница между синхронным вызовом executor и асинхронным выполнением .then()
  • Предположение, что resolve/reject внутри executor делают обработчики синхронными
  • Игнорирование того, что код после resolve внутри executor все еще выполняется (executor не останавливается после вызова resolve)

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

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