Promise глубоко

Промис изнутри: три состояния, синхронный executor, цепочки и правильная обработка ошибок.

Promise — объект-обещание будущего значения, который находится в одном из трёх состояний: pending, fulfilled или rejected.

Состояния и неизменность

Промис создаётся в состоянии pending (ожидание). Один раз он может перейти либо в fulfilled (через resolve), либо в rejected (через reject) — и больше состояние не меняется. Повторные вызовы resolve/reject игнорируются.

Функцию, которую вы передаёте в new Promise, называют executor. Важная деталь: она запускается синхронно, прямо в момент создания промиса. А вот колбэки .then — всегда асинхронно (как микрозадачи):

console.log("до new Promise");
new Promise((resolve) => {
  console.log("внутри executor (синхронно)");
  resolve();
}).then(() => console.log("then (асинхронно, microtask)"));
console.log("после new Promise");

Вывод:

до new Promise
внутри executor (синхронно)
после new Promise
then (асинхронно, microtask)

Цепочки: каждый then возвращает новый промис

То, что вы вернёте из .then, становится значением следующего звена. Если вернуть обычное значение — оно прокинется дальше; если вернуть промис — цепочка дождётся его.

const p = new Promise((resolve, reject) => {
  const ok = true;
  if (ok) resolve(10);
  else reject(new Error("сломалось"));
});

p.then(x => x * 2)
 .then(x => x + 1)
 .then(x => console.log("результат:", x));

Вывод:

результат: 21

Значение прошло путь 10 → 20 → 21. Каждое звено получило результат предыдущего.

Обработка ошибок: один catch на всю цепочку

Ошибка (или reject) «проваливается» вниз по цепочке мимо всех .then, пока не встретит .catch. После обработки цепочка продолжается в нормальном режиме:

Promise.resolve(5)
  .then(x => { throw new Error("ой"); })
  .then(x => console.log("это пропустится"))
  .catch(err => console.log("поймали:", err.message))
  .then(() => console.log("идём дальше"));

Вывод:

поймали: ой
идём дальше

Промежуточный .then со словами «это пропустится» был проигнорирован, потому что цепочка была в состоянии ошибки. .catch её погасил, и последний .then снова выполнился.

Частые ошибки

ОшибкаПочему плохо
Забыть return внутри .thenСледующее звено получит undefined, а вложенный промис не дождётся
Нет .catch в концеОшибка станет «unhandled rejection» и может молча потеряться
Вкладывать .then внутрь .then«Лесенка» вместо плоской цепочки — теряется главное преимущество промисов

Итог

  • Промис проходит pending → fulfilled/rejected ровно один раз.
  • Executor синхронен, .then — микрозадача.
  • Цепочка плоская; один .catch в конце ловит ошибки всех звеньев выше.
Проверьте себя
1. Когда выполняется функция-executor, переданная в new Promise?
AАсинхронно, как микрозадача
BСинхронно, в момент создания промиса
CТолько после первого .then
DВ следующем тике event loop
2. Что вернёт следующее звено, если в .then забыть return перед промисом?
AСам промис как объект
Bundefined — цепочка не дождётся вложенного промиса
CОшибку
DПредыдущее значение
3. Куда «провалится» ошибка, брошенная в середине цепочки .then?
AВ ближайший следующий .then
BВ ближайший следующий .catch
CПрервёт всю программу
DВ консоль и остановит цепочку навсегда
Поддержать проект