Deferred

Deferred — объект, символизирующий будущее значение, у которого resolve/reject выставлены как открытые методы наружу. Исторический предшественник Promise; сегодня прямой аналог — Promise.withResolvers.

Что это / Зачем

В эру до ES2015 (jQuery Deferred, Q.defer, когда промисов в языке не было), нужно было получить объект, у которого resolve/reject доступны снаружи, чтобы передать его в callback I/O. Промис «прячет» resolve/reject внутри executor'а — Deferred их «торчит наружу».

Сегодня тот же кейс решается через Promise.withResolvers (ES2024). Но Deferred-паттерн как идея остаётся полезным: он удобен в сценариях, когда промис создаётся в одном месте, а разрешается совсем в другом (контроллеры событий, очереди, FSM).

API / Синтаксис

// Минимальный Deferred
const defer = () => {
  let resolve, reject;
  const promise = new Promise((res, rej) => {
    resolve = res;
    reject = rej;
  });
  return { promise, resolve, reject };
};

// ES2024 — встроенный аналог
const { promise, resolve, reject } = Promise.withResolvers;

// Использование: разрешение из внешнего обработчика
const d = defer;
socket.once('message', (msg) => d.resolve(msg));
socket.once('error',   (err) => d.reject(err));
const data = await d.promise;

Ключевые моменты

  • Deferred выставляет три состояния: pending, resolved, rejected.
  • В отличие от наивного объекта-обещания, нужен флаг resolved, чтобы поздняя подписка тоже сработала (иначе подписка после разрешения «теряется»).
  • Из идеи Deferred выросли Promises.
  • Не называйте свои методы then/catch в обычных бизнес-объектах — await obj нечаянно вызовет ваш then и зависнет.
  • Современная альтернатива — Promise.withResolvers. Сторонний жанр — наследовать Deferred от EventEmitter (done/fail/on('progress')).

Подводные камни

  • Поздняя подписка на наивный Deferred (без флага resolved) пропустит уже произошедшее событие.
  • Один Deferred = одна операция. Цепочку из дифферов строить неудобно — этим занимаются полноценные Promise.
  • Утечка resolve/reject в произвольный код — антипаттерн, если только не нужно явно (мост между EventEmitter и Promise).

🎓 Источники

  • 🎓 [Deferred. Асинхронность на диферах с состоянием] · 2019-05-14 · YouTube
    • Тезисы:
      • Diferred — это объект, символизирующий будущее значение, с done/fail/resolve/reject наружу.
      • Без флага resolved поздняя подписка не сработает.
      • Из дифферов исторически развились Promise.
      • Удобно реализовать Deferred поверх EventEmitter и сделать конвертер в Promise.
    • Цитата:

      «Диффер не дружит с асинхронностью без флага resolved — поздняя подписка просто не срабатывает.»

  • ⚡ [Promise.withResolvers VS new Promise] · 2025-12-21 · YouTube

См. также