> Как создать собственную функцию executor в Promise в JavaScript (JavaScript)

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

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

Стек: JavaScript

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

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

Собственная функция executor в Promise - это функция, передаваемая в конструктор new Promise(executor). Она принимает два параметра: resolve и reject, которые являются функциями обратного вызова. Вызов resolve(value) переводит promise в состояние fulfilled, а reject(reason) - в rejected. Executor выполняется синхронно при создании promise, до того как будет возвращён сам promise-объект.

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

Executor - это сердце любого Promise. Он предоставляет механизм управления асинхронной операцией. Когда вы пишете new Promise((resolve, reject) => { ... }), эта стрелочная функция и есть executor. Важно понимать, что executor выполняется немедленно и синхронно в момент вызова конструктора Promise. Это означает, что любой код внутри executor, не являющийся асинхронным, выполняется до того, как promise будет возвращён.

Executor может вызывать resolve или reject только один раз - последующие вызовы игнорируются. Если executor выбрасывает синхронное исключение, promise автоматически переходит в состояние rejected с этим исключением. Это поведение отличает executor от обычных функций - ошибка не всплывает, а перехватывается конструктором Promise.

Executor не может возвращать значение напрямую - его результат игнорируется. Вся коммуникация происходит через resolve и reject. Это позволяет инкапсулировать асинхронную логику и предоставить единый интерфейс для обработки результата.

На практике

При создании собственного executor вы должны явно вызывать resolve или reject в нужный момент. Типичный сценарий - обёртка callback-based API, например, setTimeout, fs.readFile или XMLHttpRequest. Важно гарантировать, что вызов resolve или reject произойдёт ровно один раз, иначе promise зависнет в состоянии pending.

Executor может быть асинхронным сам по себе - вы можете использовать внутри него async/await, но это не меняет его синхронного выполнения. Если executor помечен как async, он вернёт promise, но конструктор Promise этого не ожидает - он просто игнорирует возвращаемое значение. Исключение из асинхронного executor не будет автоматически преобразовано в reject, поэтому нужно явно обрабатывать ошибки.

Пример кода

JAVASCRIPT
// Собственный executor для setTimeout
function delay(ms) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`Прошло ${ms} мс`);
}, ms);
});
}
// Executor с обработкой ошибок
function fetchData(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject(new Error(`HTTP ${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error('Network error'));
xhr.send();
});
}
// Executor с синхронным исключением
const safePromise = new Promise((resolve, reject) => {
throw new Error('Синхронная ошибка');
// Promise автоматически станет rejected
});

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

Начните с определения executor и его сигнатуры. Подчеркните, что executor выполняется синхронно - это ключевой момент, который часто упускают. Объясните, что resolve и reject - это функции, предоставляемые конструктором Promise, и их можно вызывать только один раз. Приведите пример обёртки callback-based API. Упомяните, что executor не должен быть async-функцией, если вы ожидаете автоматического перехвата ошибок. Завершите тем, что executor - это паттерн для преобразования асинхронных операций в promise-based интерфейс.

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

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

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

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

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

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