> Как создать и выполнить промис через конструктор в JavaScript (JavaScript)

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

Компании: TYMY

Стек: JavaScript

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

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

Конструктор new Promise(executor) принимает функцию-исполнитель с двумя параметрами - resolve и reject. Вызов resolve(value) переводит промис в состояние fulfilled, reject(error) - в rejected. Промис выполняется сразу при создании, асинхронно вызывая then/catch в microtask queue. Для отложенного выполнения executor может содержать асинхронные операции (setTimeout, fetch, I/O).

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

Конструктор Promise - это базовый механизм создания промисов в JavaScript. Функция executor вызывается синхронно в момент создания промиса, что позволяет инициировать асинхронную операцию. Параметры resolve и reject - это функции обратного вызова, предоставляемые средой выполнения.

resolve(value) завершает промис успешно, передавая значение в цепочку .then(). Если передать другой промис, он будет "развёрнут" - итоговый промис примет состояние переданного. reject(reason) завершает промис с ошибкой, передавая причину в .catch() или второй аргумент .then().

Важно: вызов resolve или reject после первого не имеет эффекта - промис может быть завершён только один раз. Остальной код в executor продолжает выполняться, но его результаты игнорируются. Для асинхронных операций (например, setTimeout) executor завершается до вызова resolve/reject, что является нормой.

На практике

Создание промиса через конструктор обычно требуется при обёртывании колбэк-ориентированных API (например, fs.readFile в Node.js, XMLHttpRequest в браузере). В современном коде предпочтительнее использовать async/await и встроенные промис-обёртки, но понимание конструктора критично для отладки и работы с legacy-кодом.

Типичный паттерн - обёртка функции с колбэком: new Promise((resolve, reject) => { someAsyncFunction(args, (err, data) => { if (err) reject(err); else resolve(data); }); }). При этом нужно обрабатывать ошибки синхронного кода внутри executor - если executor бросает исключение до вызова resolve/reject, промис автоматически переходит в rejected.

Пример кода

JAVASCRIPT
// Базовое создание и выполнение
const promise = new Promise((resolve, reject) => {
const success = Math.random() > 0.5;
setTimeout(() => {
if (success) {
resolve('Успех');
} else {
reject(new Error('Ошибка'));
}
}, 1000);
});
promise
.then(result => console.log(result))
.catch(error => console.error(error.message));
// Обёртка колбэка (Node.js стиль)
const fs = require('fs');
function readFilePromise(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, 'utf8', (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
// Важно: executor выполняется синхронно
console.log('Старт');
new Promise((resolve) => {
console.log('Внутри executor');
resolve('Готово');
}).then(console.log);
console.log('Финиш');
// Вывод: Старт, Внутри executor, Финиш, Готово

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

Начни с определения конструктора и его параметров. Объясни, что executor выполняется синхронно, а resolve/reject - асинхронно через microtask queue. Упомяни однократность завершения и "тихие" ошибки в executor (если не вызван reject). Приведи пример обёртки колбэка. Для senior-уровня добавь про разворачивание промисов при передаче в resolve и про то, что executor может быть асинхронным сам по себе (async-функция не рекомендуется, но возможна).

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

  • Понимание синхронного выполнения executor и асинхронного вызова then/catch.
  • Знание, что промис может быть завершён только один раз.
  • Умение обёртывать колбэк-ориентированные API.
  • Понимание разницы между microtask и macrotask (event loop).
  • Осведомлённость о "тихих" ошибках и необходимости try-catch внутри executor.

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

  • Вызов resolve/reject несколько раз (работает только первый).
  • Забывание вызвать reject при ошибке в колбэке - промис остаётся в pending.
  • Использование async-функции как executor без обработки ошибок (async-функция возвращает промис, который не будет перехвачен конструктором).
  • Предположение, что executor выполняется асинхронно - на самом деле он синхронный.
  • Необработка синхронных исключений внутри executor (они автоматически переводят промис в rejected, но только если не было вызова resolve до них).

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

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