$ sudo teach IT
МОДУЛЬ 10 · УРОК 3

Промисы (Promise)

Промисы — это элегантное решение callback hell. Вместо бесконечных вложенных функций, вы работаете с «обещаниями» — объектами, которые представляют будущий результат асинхронной операции. Понимание промисов — ключ к современному JavaScript.

⏱ ~25 минут 🎓 Средний ⚡ Асинхронность

🤝 Promise — объект-обещание

Представьте, что вы заказали пиццу. Вам дали чек — это «обещание» (promise), что пицца будет. Пока пицца готовится, вы можете делать другие дела. Когда приготовят — вам скажут «выполнено» и отдадут пиццу. Если что-то пошло не так — скажут «ошибка».

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

⏳
pending

Ожидание — операция ещё выполняется. Начальное состояние.

✅
fulfilled

Выполнен успешно — результат готов. Вызвана функция resolve.

❌
rejected

Завершён с ошибкой. Вызвана функция reject.

🔑 Важно: промис может перейти из pending только в одно из двух финальных состояний — fulfilled или rejected. Изменить состояние назад невозможно. Это гарантирует предсказуемость.

🏗️ Создаём Promise

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

// Простой промис — эмулируем загрузку данных
const myPromise = new Promise((resolve, reject) => {
  // Имитируем задержку в 1 секунду (как запрос к серверу)
  setTimeout(() => {
    const success = true; // представим, что загрузка успешна

    if (success) {
      resolve('Данные загружены!'); // выполнен успешно
    } else {
      reject(new Error('Ошибка загрузки!')); // завершён с ошибкой
    }
  }, 1000);
});

console.log(myPromise); // Promise { <pending> } — пока ждёт

⛓️ .then(), .catch(), .finally()

Чтобы работать с результатом промиса, используют методы .then(), .catch() и .finally(). Они возвращают новые промисы — поэтому их можно цеплять в цепочку.

myPromise
  .then((result) => {
    // Успех: result = 'Данные загружены!'
    console.log('Успех:', result);
    return result.toUpperCase(); // можем вернуть новое значение
  })
  .then((upperResult) => {
    // Получили результат из предыдущего .then()
    console.log('В верхнем регистре:', upperResult);
  })
  .catch((error) => {
    // Обрабатываем ошибку — catch ловит ошибки со ВСЕЙ цепочки
    console.error('Ошибка:', error.message);
  })
  .finally(() => {
    // Выполнится ВСЕГДА — и при успехе, и при ошибке
    console.log('Промис завершён (скрываем лоадер)');
  });
.then(fn)

Вызывается при успехе. Получает значение из resolve(). Можно вернуть новый промис или значение.

.catch(fn)

Вызывается при ошибке. Ловит ошибки из reject() и брошенных исключений во всей цепочке.

.finally(fn)

Выполняется всегда — независимо от результата. Идеально для скрытия лоадера.

🏁 Promise.all() и Promise.race()

Иногда нужно запустить несколько асинхронных операций одновременно. Для этого есть статические методы:

// Promise.all — ждёт ВСЕ промисы
// Если хоть один отклонён — весь Promise.all отклоняется
const p1 = fetch('/api/users');
const p2 = fetch('/api/products');
const p3 = fetch('/api/orders');

Promise.all([p1, p2, p3])
  .then(([users, products, orders]) => {
    console.log('Все данные загружены');
  })
  .catch(err => console.error('Один из запросов упал:', err));

// Promise.race — ждёт ПЕРВЫЙ завершённый промис
// Побеждает тот, кто ответил быстрее
Promise.race([
  fetch('/api/fast'),
  fetch('/api/slow')
]).then(result => {
  console.log('Первый ответ:', result);
});

💡 Также есть Promise.allSettled() — ждёт все промисы и возвращает результаты каждого (даже если некоторые отклонены). Полезно когда нужны все результаты независимо от ошибок.

✅ Итоги урока

  • Promise — объект-обещание с тремя состояниями: pending, fulfilled, rejected
  • new Promise((resolve, reject) => {...}) — создание промиса
  • resolve(value) — успешное завершение, reject(error) — ошибка
  • .then(fn) — обработка успеха, .catch(fn) — обработка ошибки
  • .finally(fn) — выполнится всегда независимо от результата
  • Промисы можно цеплять в цепочку — каждый .then() возвращает новый промис
  • Promise.all([]) — ждёт все промисы, Promise.race([]) — ждёт первый

В следующем уроке разберём async/await — синтаксический сахар над промисами, который делает асинхронный код похожим на синхронный. 🚀

Промисы (Promise)

7 вопросов

Функция delay

Реализуйте функцию delay(ms), которая возвращает Promise, выполняющийся через ms миллисекунд. Затем используйте её, чтобы реализовать функцию waitAndSay(message, ms), которая ждёт ms миллисекунд и выводит message в консоль.

Цепочка промисов

Реализуйте цепочку промисов: первый промис возвращает число 5, второй умножает его на 2, третий прибавляет 10. Используйте .then() для построения цепочки. Итоговый результат должен быть выведен в консоль.