> Как работает конструктор Promise, синхронность создания и асинхронность резолва в JavaScript (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: amoCRM, GoInvest, Яндекс
Стек: JavaScript
> Пример ответа
Короткий ответ
Конструктор Promise принимает executor-функцию, которая выполняется синхронно в момент создания промиса. Внутри executor вызываются resolve/reject - они лишь переводят промис в соответствующее состояние, но не выполняют коллбэки .then/.catch. Асинхронность резолва обеспечивается тем, что коллбэки попадают в microtask queue и выполняются после завершения текущего синхронного кода, но до макрозадач (setTimeout, I/O).
Подробное объяснение
Конструктор new Promise(executor) вызывает executor немедленно и синхронно - это часть спецификации ECMAScript (раздел 25.6.3.1). Внутри executor может быть любой код: синхронный (например, вызов resolve("ok")) или асинхронный (setTimeout, fetch, event listener).
Когда resolve или reject вызываются синхронно внутри executor, промис переходит в состояние fulfilled/rejected в том же синхронном потоке. Однако коллбэки, зарегистрированные через .then(), .catch(), .finally(), не вызываются сразу - они ставятся в очередь microtask. Это гарантирует, что код после создания промиса выполнится до обработки результата.
Если executor содержит асинхронную операцию (например, setTimeout с resolve внутри), то resolve вызовется позже, в момент срабатывания таймера. Тогда промис перейдёт в нужное состояние, и коллбэки попадут в microtask queue.
Важно: resolve/reject могут быть вызваны только один раз - последующие вызовы игнорируются. Состояние промиса фиксируется при первом вызове.
На практике
Понимание синхронности executor критично для отладки и предотвращения race conditions. Например, если внутри executor сразу вызывается resolve, то .then() всё равно выполнится асинхронно - это позволяет избежать проблем с порядком выполнения, когда промис создаётся до регистрации обработчиков.
Распространённый паттерн - оборачивание callback-based API в промис:
JAVASCRIPTfunction readFilePromise(path) {return new Promise((resolve, reject) => {fs.readFile(path, (err, data) => {if (err) reject(err);else resolve(data);});});}
Здесь executor синхронно запускает fs.readFile, но resolve/reject вызываются асинхронно, внутри коллбэка.
Другой пример - создание уже зарезолвленного промиса через Promise.resolve(). Это удобно для нормализации значений: Promise.resolve(5) создаёт промис, который сразу переходит в fulfilled, но .then() всё равно выполнится в microtask.
Пример кода
JAVASCRIPTconsole.log('1: start');const promise = new Promise((resolve) => {console.log('2: executor start');resolve('3: resolved');console.log('4: executor end');});promise.then((value) => {console.log('5:', value);});console.log('6: after promise creation');// Вывод:// 1: start// 2: executor start// 4: executor end// 6: after promise creation// 5: 3: resolved
Обратите внимание: executor выполняется полностью синхронно (2, 4), затем синхронный код продолжается (6), и только потом выполняется коллбэк .then (5) - это и есть асинхронность резолва через microtask.
Как отвечать на собеседовании
Начни с ключевого факта: executor выполняется синхронно. Приведи пример с порядком вывода, чтобы наглядно показать разницу между синхронным созданием и асинхронным выполнением коллбэков. Упомяни, что resolve/reject - это не вызов .then, а лишь изменение состояния промиса. Объясни роль microtask queue и отличие от макрозадач. Если спросят про Promise.resolve(), отметь, что он тоже создаёт промис, но коллбэки всё равно идут в microtask.
Что проверяет интервьюер
- Понимание event loop и очередей (microtask vs macrotask)
- Знание спецификации Promise: синхронность executor, однократность resolve/reject
- Умение предсказывать порядок выполнения кода с промисами
- Навыки работы с асинхронными паттернами (промисфикация callback'ов)
Типичные ошибки
- Утверждение, что resolve/reject вызывают .then синхронно
- Путаница между синхронным выполнением executor и асинхронным вызовом коллбэков
- Игнорирование того, что код после resolve внутри executor всё равно выполняется
- Предположение, что
Promise.resolve()выполняет .then немедленно - Забывание про однократность вызова resolve/reject - попытка повторного вызова не меняет состояние
> Похожие задачи по frontend
Как реализовать функцию auth через колбек в JavaScript
Что такое метод map в JavaScript и как он работает
Почему новый объект не изменяет исходный объект при передаче в функцию в JavaScript
Можно ли сравнить два объекта в JavaScript и как это сделать
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью