Promise: состояние pending, fulfilled, rejected

Что такое Promise и зачем нужны состояния? 🤔

Promise (Обещание) — это объект, представляющий результат асинхронной операции. Он может находиться в одном из трёх состояний:

  • pending (ожидание) — начальное состояние, операция ещё не завершена
  • fulfilled (выполнено) — операция успешно завершена
  • rejected (отклонено) — операция завершена с ошибкой

Представьте, что Promise — это пицца, которую вы заказали. Пока её готовят — это pending. Когда привозят горячую и вкусную — fulfilled. А если курьер уронил коробку — увы, rejected.


Как работает состояние pending

Каждый Promise начинается со состояния pending. В этот момент асинхронная операция ещё выполняется, и мы не знаем, чем всё закончится.

const pizzaPromise = new Promise((resolve, reject) => {
  // Здесь пицца ещё готовится
}); 

console.log(pizzaPromise); // Promise { <pending> }

💡 Проверить состояние можно через .then(), .catch() или finally(), но сам объект Promise не предоставляет прямого доступа к текущему состоянию.


Переход в fulfilled — успешное выполнение 🎉

Когда асинхронная операция завершается успешно, Promise переходит в состояние fulfilled, вызывая функцию resolve:

const successPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Пицца доставлена!"); // Переход в fulfilled
  }, 2000);
});

successPromise.then(result => {
  console.log(result); // "Пицца доставлена!"
});

После перехода в fulfilled Promise запоминает своё значение навсегда (это называется "урегулирование" Promise).


Переход в rejected — когда что-то пошло не так 💥

Если в процессе выполнения возникает ошибка, Promise переходит в состояние rejected через функцию reject:

const failedPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error("Курьер попал в пробку!")); // Переход в rejected
  }, 2000);
});

failedPromise.catch(error => {
  console.error(error.message); // "Курьер попал в пробку!"
});

Как и с fulfilled, после перехода в rejected Promise больше не может изменить своё состояние.


Жизненный цикл Promise в картинках 🖼️

Вот как выглядит полный цикл жизни Promise:

          +-------------------+
          |      pending       |
          +---------+---------+
                          |
          +---------v---------+
          |  fulfilled (resolve) |
          +-------------------+
                          |
          +---------v---------+
          |  rejected (reject)  |
          +-------------------+

🎯 Важно: Promise может перейти ТОЛЬКО ИЗ pending в ЛИБО fulfilled, ЛИБО rejected. Обратного пути нет!


Практический пример: загрузка данных с сервера 🌐

Давайте рассмотрим реальный пример с fetch, который возвращает Promise:

const loadUserData = (userId) => {
  return new Promise((resolve, reject) => {
    fetch(`https://api.example.com/users/${userId}`)
      .then(response => {
        if (!response.ok) {
          reject(new Error('Пользователь не найден'));
        }
        return response.json();
      })
      .then(data => resolve(data))
      .catch(error => reject(error));
  });
};

// Использование
loadUserData(123)
  .then(user => console.log('Данные:', user))
  .catch(err => console.error('Ошибка:', err));

Здесь мы видим все три состояния:

  1. pending — пока идёт запрос
  2. fulfilled — если сервер вернул данные
  3. rejected — если возникла ошибка

Важные особенности состояний 🧠

1. Одноразовость: Promise может изменить состояние только один раз. Последующие вызовы resolve/reject игнорируются.

const promise = new Promise((resolve, reject) => {
  resolve('Первый вызов');
  reject('Второй вызов'); // Игнорируется!
  resolve('Третий вызов'); // Тоже игнорируется!
});

2. Немедленное урегулирование: Можно создать уже разрешённый Promise:

const fulfilledPromise = Promise.resolve('Готово сразу!');
const rejectedPromise = Promise.reject(new Error('Сразу ошибка'));

3. Цепочки Promise: Каждый .then() возвращает новый Promise, что позволяет строить цепочки.


Как избежать "зависших" Promise? 🛑

Promise, который никогда не выходит из состояния pending, может вызвать утечки памяти. Всегда предусматривайте завершение:

// Плохо — Promise может зависнуть
const unsafePromise = new Promise(() => {
  // Нет вызова resolve/reject
});

// Хорошо — таймаут на завершение
const safePromise = new Promise((resolve, reject) => {
  const timer = setTimeout(() => {
    reject(new Error('Таймаут выполнения'));
  }, 5000);

  // Какая-то асинхронная операция
  someAsyncOperation()
    .then(resolve)
    .catch(reject)
    .finally(() => clearTimeout(timer));
});

Подведём итоги 🏁

  • Promise — это "контейнер" для асинхронной операции с тремя состояниями
  • Начинается всегда с pending, затем переходит в fulfilled — при успешном выполнении (вызов resolve) и rejected — при ошибке (вызов reject)
  • Состояние меняется только один раз и навсегда
  • Используйте .then(), .catch() и finally() для реакции на изменения состояния

Теперь вы понимаете "анатомию" Promise настолько хорошо, что сможете объяснить её даже своему коту! Ну, или коллеге — кому как повезёт больше. 🐱‍💻

Скрыть рекламу навсегда

📘 VK Видео — обучение без ограничений

Все уроки доступны без VPN, без блокировок и зависаний.

Можно смотреть с телефона, планшета или компьютера — в любое время.

▶️ Смотреть на VK Видео