UNPKG

@johngw/stream

Version:

Reactive programming tools using the WHATWG Streams API.

84 lines 2.35 kB
import { without } from '@johngw/stream-common/Array'; /** * The underlying source of a ReadableStream that can have chunks * queued to from an external source. * * @group Sources * @example * Queuing items externally. * * ``` * const controller = new ControllableSource<number>() * const stream = new ReadableStream(controller) * controller.enqueue(1) * controller.enqueue(2) * controller.enqueue(3) * controller.close() * ``` * * Registering pull subscribers externally. * * ``` * const controller = new ControllableStream<number>() * let i = -1 * controller.onPull(() => ++i) * ``` */ export class ControllableSource { #controller; #pullListeners = []; start(controller) { this.#controller = controller; } async pull(controller) { if (!this.#pullListeners.length) return; /* At first glance you may wonder why we're not using Promise.all. This is because we have no idea how long each pullListener is going to take and we may want to fill the desired size before all listeners have resolved. Therefore we resolve when the 1st listener queues an item so the stream can request more if it needs. */ return new Promise((resolve) => { this.#pullListeners.forEach(async (pullListener) => { try { controller.enqueue(await pullListener()); } catch (error) { controller.error(error); } finally { resolve(); } }); }); } get desiredSize() { return this.#controller.desiredSize; } /** * Register a pull subscriber. * * @remark * When the stream is ready to pull it will pull from all * subscribers until the desired size has been fulfilled. */ onPull(pullListener) { this.#pullListeners.push(pullListener); return () => { this.#pullListeners = without(this.#pullListeners, pullListener); }; } error(error) { this.#controller.error(error); } enqueue(chunk) { this.#controller.enqueue(chunk); } close() { this.#controller.close(); } } //# sourceMappingURL=ControllableSource.js.map