← Назад к вопросам

Что такое контракт thennable?

1.7 Middle🔥 171 комментариев
#JavaScript Core

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое контракт Thennable?

Контракт Thennable — это неформальное, но критически важное соглашение в JavaScript, которое определяет минимальный интерфейс, необходимый объекту для работы с механизмом Promise. Объект, соответствующий этому контракту, называется thennable (произносится как "зенэйбл", от англ. "thenable" – "имеющий метод then").

Суть контракта

Контракт гласит: объект является thennable, если он имеет метод .then(), который принимает два аргумента (колбэка) – onFulfilled и onRejected. Этот метод должен работать по тем же принципам, что и у нативного Promise:

thennableObject.then(
  function onFulfilled(value) {
    // Вызывается, когда thennable переходит в состояние "выполнено"
  },
  function onRejected(reason) {
    // Вызывается, когда thennable переходит в состояние "отклонено"
  }
);

Зачем это нужно?

  1. Интеграция с экосистемой Promise: Механизмы, работающие с промисами (например, Promise.resolve(), async/await, Promise.all()), умеют работать не только с нативными Promise, но и с любыми thennable-объектами. Это обеспечивает обратную совместимость и гибкость.

  2. Ассимиляция других реализаций промисов: До стандартизации Promise в ES2015 существовало множество библиотечных реализаций (Q, Bluebird, $q в Angular.js). Контракт thennable позволяет им работать вместе с нативными промисами.

  3. Создание адаптеров и "ленивых" промисов: Вы можете создавать объекты, которые выглядят как промисы, но вычисляют значение только при вызове .then(). Например, для отложенной загрузки данных.

Пример реализации простого Thennable

// Простейший объект, соответствующий контракту thennable
const myThennable = {
  then(onFulfilled, onRejected) {
    // Симулируем асинхронное выполнение
    setTimeout(() => {
      const success = true;
      if (success) {
        // Вызываем колбэк успеха с результатом
        onFulfilled('Данные загружены!');
      } else {
        // Вызываем колбэк ошибки с причиной
        onRejected(new Error('Ошибка загрузки'));
      }
    }, 100);
  }
};

// Нативный Promise.resolve() умеет работать с thennable!
Promise.resolve(myThennable)
  .then(data => console.log(data)) // Выведет: "Данные загружены!"
  .catch(err => console.error(err));

Важные особенности и отличия от нативного Promise

  • Состояния: Как и промис, thennable должен управлять тремя состояниями: pending (ожидание), fulfilled (выполнено), rejected (отклонено).
  • Одноразовость: Результат должен быть зафиксирован. После выполнения или отклонения последующие вызовы .then() должны возвращать тот же результат.
  • Асинхронность: Вызов колбэков onFulfilled/onRejected должен происходить асинхронно, после очистки стека вызовов (например, через setTimeout(fn, 0), queueMicrotask() или Promise.resolve().then()). Это критически важно для предсказуемости порядка выполнения кода.
  • Цепочка вызовов: Метод .then() должен возвращать новый thennable (или Promise). Это позволяет строить цепочки:
myThennable
  .then(val => val.toUpperCase())
  .then(val => console.log(val)); // Цепочка возможна, только если .then() возвращает thennable

Где встречается на практике?

  1. jQuery Deferred: Объекты $.Deferred() (и их производные, например, jqXHR от $.ajax()) являются классическими thennable.

    const jqPromise = $.ajax('/api/data');
    // Превращаем jQuery Deferred в нативный Promise
    const nativePromise = Promise.resolve(jqPromise);
    
  2. Совместимые библиотеки: Bluebird, Q и другие предоставляют объекты, которые являются thennable и могут быть использованы там, где ожидается нативный Promise.

  3. Web API: Некоторые API, разработанные до широкого внедрения промисов, могут возвращать thennable-объекты для совместимости.

Потенциальные проблемы

  • Неполная совместимость: Если thennable реализован некорректно (например, вызывает колбэки синхронно или многократно), это может привести к трудноотлавливаемым ошибкам и нарушению порядка выполнения.
  • Сложность отладки: Thennable-объекты не обязаны иметь методы .catch() или .finally(), а также статические методы типа Promise.all. Отладка может быть менее удобной.

Вывод: Контракт thennable — это краеугольный камень асинхронного программирования в JavaScript, который обеспечивает интероперабельность между различными реализациями асинхронных примитивов. Понимание этого контракта позволяет глубже разобраться в работе промисов, создавать совместимые библиотеки и понимать, как старый код интегрируется с новыми стандартами. Однако в современной разработке, если нет необходимости в интеграции со старыми библиотеками, рекомендуется использовать нативные Promise или async/await для гарантии предсказуемости и наличия всех инструментов разработчика.

Что такое контракт thennable? | PrepBro