> Может ли Promise одновременно зарезолвиться и зареджектиться в JavaScript? (JavaScript)

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

Компании: VK

Стек: JavaScript

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

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

Нет, Promise не может одновременно зарезолвиться и зареджектиться. Promise может находиться только в одном из трёх состояний: pending, fulfilled или rejected. После перехода в fulfilled или rejected состояние становится окончательным (settled) и не может быть изменено. Даже если вызвать resolve и reject в одном потоке, выполнится только первый вызов, второй будет проигнорирован.

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

Promise в JavaScript реализует конечный автомат с тремя состояниями: pending (ожидание), fulfilled (выполнен успешно) и rejected (отклонён). Переход между состояниями возможен только один раз: из pending в fulfilled или из pending в rejected. После перехода состояние фиксируется - это называется settled.

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

Важно понимать, что resolve и reject могут быть вызваны асинхронно, но даже в этом случае первое завершённое действие определяет конечное состояние. Например, если reject вызван в setTimeout, а resolve - синхронно, то Promise зарезолвится, а reject будет проигнорирован.

На практике

На практике это свойство Promise используется для гарантии того, что обработчик then или catch выполнится ровно один раз. Это предотвращает состояние гонки (race condition) в асинхронном коде. Разработчики могут безопасно вызывать resolve и reject в разных ветках логики, не беспокоясь о двойном выполнении.

Однако стоит помнить, что если оба вызова происходят синхронно, второй будет просто проигнорирован без ошибки. Это может скрыть баги в коде, когда разработчик ошибочно предполагает, что оба вызова обработаются.

Пример кода

JAVASCRIPT
const promise = new Promise((resolve, reject) => {
resolve('успех');
reject('ошибка'); // игнорируется
});
promise
.then(value => console.log('resolve:', value))
.catch(error => console.log('reject:', error));
// Вывод: resolve: успех
JAVASCRIPT
// Асинхронный случай
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject('таймаут'), 100);
resolve('быстрый успех');
});
promise2
.then(value => console.log('resolve:', value))
.catch(error => console.log('reject:', error));
// Вывод: resolve: быстрый успех

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

Начните с чёткого отрицательного ответа. Затем объясните модель состояний Promise и гарантию однократного перехода. Подчеркните, что это фундаментальное свойство, обеспечивающее предсказуемость асинхронного кода. Приведите пример с синхронным и асинхронным вызовом. Упомяните, что игнорирование второго вызова может маскировать ошибки, поэтому стоит использовать линтеры или TypeScript для контроля.

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

Интервьюер проверяет понимание модели состояний Promise, спецификации ECMAScript и практических последствий этого поведения. Также оценивается способность объяснить, почему это важно для надёжности асинхронного кода и как избежать скрытых багов.

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

  • Утверждение, что Promise может быть одновременно в двух состояниях.
  • Предположение, что второй вызов вызовет исключение или ошибку.
  • Непонимание, что асинхронные вызовы также подчиняются правилу первого перехода.
  • Путаница между состоянием Promise и результатом его выполнения (например, думать, что reject после resolve изменит результат).

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

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