Thenable
Thenable — любой объект с методом
then(onFulfilled, onRejected). С ним работаютawaitиPromise.resolve, что позволяет строить кастомные «промисоподобные» абстракции, не наследуясь отPromise.
Что это / Зачем
Promise — это лишь одна реализация контракта thenable. Если у объекта есть then(onFulfilled, onRejected?), его можно await-ить и передавать в Promise.resolve — он будет ассимилирован. Это нужно, чтобы:
- сделать собственный лёгкий аналог Promise без лишних аллокаций;
- интегрировать сторонние "промисоподобные" объекты (jQuery Deferred, библиотеки Bluebird, Q);
- построить итерируемую асинхронную абстракцию, где состояние меняется многократно (Promise может разрешиться только один раз).
API / Синтаксис
// Минимальный thenable
const t = {
then(onFulfilled) { onFulfilled(5); },
};
const x = await t; // 5
// Класс с отложенным значением
class Result {
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled;
this.onRejected = onRejected;
}
ready(data) { this.onFulfilled && this.onFulfilled(data); }
fail(error) { this.onRejected && this.onRejected(error); }
}
// thenable, который можно await-ить в цикле (повторно меняет состояние)
class Stream {
constructor(arr) { this.state = arr; }
then(onFulfilled, onRejected) {
if (this.state.length) onFulfilled(this.state.shift());
else onRejected(new Error('empty'));
return this; // чейнинг без новых объектов
}
}
Ключевые моменты
- Promise сам построен на контракте thenable — это та же абстракция.
await thenableвызывает.then(onFulfilled, onRejected)и приостанавливает корутину до вызова одного из колбэков.- Thenable может разрешаться многократно — в отличие от Promise, состояние не фиксируется. Это позволяет
awaitв цикле над одним объектом. - Возвращая
thisизthen, экономим аллокации при чейнинге (за это платим иммутабельностью). - Для двунаправленного контракта используются
onFulfilledиonRejected.
Подводные камни
Promise.resolve(thenable)"ассимилирует" thenable — если ваш.thenсделан криво (вызывает колбэк синхронно или дважды), это сломает гарантии Promise.- Имена
thenиcatchлучше не давать обычным методам бизнес-объектов —await objнеожиданно вызоветthenи зависнет. - Полный контракт —
.then(onFulfilled, onRejected). Без второго аргумента ошибки в thenable теряются.
🎓 Источники
- 🎓 [Thenable и легковесный await в JavaScript] · 2019-05-07 · YouTube
- Тезисы:
- Thenable — объект с методом
then, работает с await. - Каждый
awaitдёргаетthenзаново — состояние можно менять. - Promise построен на том же контракте, но фиксирует состояние после первого resolve.
- Цепочка thenable через возврат
this— односвязный список без лишних объектов.
- Thenable — объект с методом
- Цитата:
«Каждый раз, когда мы будем использовать await, это состояние мы можем менять. То есть тут существенное отличие от promise, который один раз свое состояние поменял, и все.»
- Цитата:
«Мы при помощи контракта thenable реализовали практически самый простой promise. То есть это реализация promise, написанная просто на контракте thenable.»
- Тезисы:
- 🎓 [JavaScript Callable, Thenable, Promise, Array-like, Iterable, Observable, Streamable] · 2025-10-24 · YouTube
- Тезис: thenable — один из шести контрактов "поведенческих интерфейсов" JS (наряду с callable, iterable, observable…).