UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

82 lines (81 loc) 2.95 kB
import { getDeferred } from "../util/async.js"; import { AbstractSequence } from "./AbstractSequence.js"; /** Used when the deferred sequence has no value or reason queued. */ const _NOVALUE = Symbol("shelving/DeferredSequence.NOVALUE"); /** * Deferred sequence of values that can be async iterated and new values can be published. * - Implements `AsyncIterable` so values can be iterated over using `for await...of` * - Implements `Promise` so the next value can be awaited. * - Implements `Deferred` so next values can be resolved or rejected. */ export class DeferredSequence extends AbstractSequence { /** * Next deferred to be rejected/resolved, or `undefined` if we haven't requested one yet.. * - Only create the deferred on demand, because we don't want to reject a deferred that isn't used to or it would throw an unhandled promise error. */ _deferred; /** Get the next promise to be deferred/rejected. */ get promise() { this._deferred ||= getDeferred(); return this._deferred.promise; } /** Resolve the current deferred in the sequence. */ resolve(value) { this._nextValue = value; this._nextReason = _NOVALUE; queueMicrotask(() => this._fulfill()); } _nextValue = _NOVALUE; /** Reject the current deferred in the sequence. */ reject(reason) { this._nextValue = _NOVALUE; this._nextReason = reason; queueMicrotask(() => this._fulfill()); } _nextReason = _NOVALUE; /** Cancel the current resolution or rejection. */ cancel() { this._nextValue = _NOVALUE; this._nextReason = _NOVALUE; } /** Fulfill the current deferred by resolving or rejecting it. */ _fulfill() { const _deferred = this._deferred; const _nextReason = this._nextReason; const _nextValue = this._nextValue; this._nextReason = _NOVALUE; this._nextValue = _NOVALUE; if (_deferred) { if (_nextReason !== _NOVALUE) { this._deferred = undefined; _deferred.reject(_nextReason); } else if (_nextValue !== _NOVALUE) { this._deferred = undefined; _deferred.resolve(_nextValue); } } } // Implement `AsyncIterator` async next() { return { value: await this.promise }; } // Implement `Promise` // biome-ignore lint/suspicious/noThenProperty: This is intentional. then(onNext, onError) { return this.promise.then(onNext, onError); } catch(onError) { return this.promise.catch(onError); } finally(onFinally) { return this.promise.finally(onFinally); } /** Resolve the current deferred from a sequence of values. */ async *through(sequence) { for await (const item of sequence) { this.resolve(item); yield item; } } }