UNPKG

suspenders-js

Version:

Asynchronous programming library utilizing coroutines, functional reactive programming and structured concurrency.

714 lines 23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SharedEventFlow = exports.StateSubject = exports.EventSubject = exports.SharedStateFlow = exports.flowOfValues = exports.flowOf = exports.Flow = void 0; const Channel_1 = require("./Channel"); const Errors_1 = require("./Errors"); const ObserverFunction_1 = require("./ObserverFunction"); const Scope_1 = require("./Scope"); /** * Abstract class for emitting multiple values. A cold flow is doesn't start producing values until * it is yield in a coroutine with .collect() or .collectLatest(). Sharing a cold flow with multiple * coroutines requires converting it into a SharedEventSubject or SharedStateSubject. Hot flows * are EventSubjects and StateSubjects. */ class Flow { /** * Converts values emitted using mapper function. * @param {(value) => R} mapper * @returns {Flow<R>} */ map(mapper) { return new MapFlow(this, mapper); } /** * Values that don't return true from predicate function are not emitted downstream. * @param predicate */ filter(predicate) { return new FilterFlow(this, predicate); } /** * Runs binder on each emitted value and combines outputs into a single flow. * @param binder */ mergeMap(binder) { return new MergeMapFlow(this, binder); } /** * Runs f on each value but doesn't change values emitted in downstream flow. * @param f */ onEach(f) { return new OnEachFlow(this, f); } /** * Consumes values in upstream Flow. Shares values with downstream flow. Replays last value * emitted on new observers. * @returns {Flow<T>} */ sharedState() { return new SharedStateFlow(this); } /** * Consumes values in upstream Flow. Shares values with downstream flow. * @returns {Flow<T>} */ sharedEvent() { return new SharedEventFlow(this); } /** * Collects Flow in scope, ignoring emitted values. * @param scope */ launchIn(scope) { const that = this; scope.launch(function* () { yield that.collect(() => { }); }); } /** * Consumes Flow in scope. Runs collector function on emitted values. * @param {(value: T) => void} collector * @returns {Suspender<void>} */ collect(collector) { return (resultCallback) => { const observerFunction = new ObserverFunction_1.ObserverFunction(collector, () => { resultCallback({ value: undefined }); }, (error) => { resultCallback({ tag: `error`, error }); }); this.addObserver(observerFunction); return () => { this.removeObserver(observerFunction); }; }; } /** * Consumes Flow in scope. Runs collector coroutine on emitted values. Cancels previously started * coroutine if it has not completed. * @param {(value: T) => CoroutineFactory<void>} coroutineFactory */ collectLatest(coroutineFactory) { return (resultCallback) => { let cancelFunction; const scope = new Scope_1.Scope({ errorCallback: (error) => { resultCallback({ tag: `error`, error }); } }); const observer = new ObserverFunction_1.ObserverFunction((value) => { if (cancelFunction !== undefined) { cancelFunction(); } cancelFunction = scope.launch(coroutineFactory(value)); }, () => { resultCallback({ value: undefined }); }, (error) => { resultCallback({ tag: `error`, error }); }); this.addObserver(observer); return () => { this.removeObserver(observer); if (cancelFunction !== undefined) { cancelFunction(); } }; }; } /** * Runs coroutine on each value and emits values through observer. Waits for each * coroutine to complete before starting the next. Because there is no backpressure with flow, use * care to make sure that emitted values don't buffer uncontrollably. * @param transformer */ transform(transformer) { return new TransformFlow(this, transformer); } /** * Errors thrown upstream are caught and passed into coroutine factory. Resumes sending values * emitted to observer. * @param factory */ catch(factory) { return new TransformCatch(this, factory); } /** * Runs a coroutine on each value and emits values through observer. Cancels previous coroutine if * it has not completed. * @param transformer */ transformLatest(transformer) { return new TransformLatestFlow(this, transformer); } } exports.Flow = Flow; class MapFlow extends Flow { constructor(_flow, _mapper) { super(); this._flow = _flow; this._mapper = _mapper; this._hasCompleted = false; } addObserver(observer) { if (this._observer !== undefined) { throw new Errors_1.FlowConsumedError(); } this._observer = observer; this._flow.addObserver(this); } removeObserver(observer) { if (this._observer !== observer) { throw new Errors_1.FlowRemoveObserverError(); } this._flow.removeObserver(this); } emit(value) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._observer.emit(this._mapper(value)); } complete() { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._observer.complete(); } error(error) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._observer.error(error); } } class FilterFlow extends Flow { constructor(_flow, _predicate) { super(); this._flow = _flow; this._predicate = _predicate; this._hasCompleted = false; } addObserver(observer) { if (this._observer !== undefined) { throw new Errors_1.FlowConsumedError(); } this._observer = observer; this._flow.addObserver(this); } removeObserver(observer) { if (this._observer !== observer) { throw new Errors_1.FlowRemoveObserverError(); } this._flow.removeObserver(this); } emit(value) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } if (this._predicate(value)) { this._observer.emit(value); } } complete() { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._observer.complete(); } error(error) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._observer.error(error); } } class MergeMapFlow extends Flow { constructor(_flow, _binder) { super(); this._flow = _flow; this._binder = _binder; this._hasCompleted = false; this._flows = new Map(); } addObserver(observer) { if (this._observer !== undefined) { throw new Errors_1.FlowConsumedError(); } this._observer = observer; this._flow.addObserver(this); } removeObserver(observer) { if (this._observer !== observer) { throw new Errors_1.FlowRemoveObserverError(); } this._flow.removeObserver(this); } emit(value) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } const observerFunction = new ObserverFunction_1.ObserverFunction((value) => { this._observer.emit(value); }, () => { this._flows.delete(observerFunction); if (this._hasCompleted && this._flows.size === 0) { this._observer.complete(); } }, (error) => { this._flows.delete(observerFunction); this.error(error); }); const flow = this._binder(value); this._flows.set(observerFunction, flow); flow.addObserver(observerFunction); } complete() { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; } error(error) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; for (const [observerFunction, flow] of this._flows) { flow.removeObserver(observerFunction); } this._observer.error(error); } } class OnEachFlow extends Flow { constructor(_flow, _onEach) { super(); this._flow = _flow; this._onEach = _onEach; this._hasCompleted = false; } addObserver(observer) { if (this._observer !== undefined) { throw new Errors_1.FlowConsumedError(); } this._observer = observer; this._flow.addObserver(this); } removeObserver(observer) { if (this._observer !== observer) { throw new Errors_1.FlowRemoveObserverError(); } this._flow.removeObserver(this); } emit(value) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._onEach(value); this._observer.emit(value); } complete() { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._observer.complete(); } error(error) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._observer.error(error); } } class TransformFlow extends Flow { constructor(_flow, _transformerFactory) { super(); this._flow = _flow; this._transformerFactory = _transformerFactory; this._hasCompleted = false; } addObserver(observer) { if (this._observer !== undefined) { throw new Errors_1.FlowConsumedError(); } this._observer = observer; this._scope = new Scope_1.Scope({ errorCallback: (error) => { observer.error(error); } }); this._receiverChannel = new Channel_1.Channel({ bufferSize: Infinity }); this._observerFunction = new ObserverFunction_1.ObserverFunction((value) => { observer.emit(value); }, () => { if (this._hasCompleted) { observer.complete(); } }, (error) => { this.error(error); }); const that = this; this._scope.launch(function* () { while (!that._hasCompleted) { yield* this.call(that._transformerFactory(yield* this.suspend(that._receiverChannel.receive), that._observerFunction)); } }); this._flow.addObserver(this); } removeObserver(observer) { if (this._observer !== observer) { throw new Errors_1.FlowRemoveObserverError(); } this._flow.removeObserver(this); this._scope.cancel(); } emit(value) { if (this._receiverChannel === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } // channel buffer is Infinite so we don't check for failure this._receiverChannel.trySend(value); } complete() { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; } error(error) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._scope.cancel(); this._observer.error(error); } } class TransformCatch extends Flow { constructor(_flow, _transformerFactory) { super(); this._flow = _flow; this._transformerFactory = _transformerFactory; this._hasCompleted = false; } addObserver(observer) { if (this._observer !== undefined) { throw new Errors_1.FlowConsumedError(); } this._observer = observer; this._flow.addObserver(this); } removeObserver(observer) { if (this._observer !== observer) { throw new Errors_1.FlowRemoveObserverError(); } this._flow.removeObserver(this); this._scope?.cancel(); } emit(value) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._observer.emit(value); } complete() { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._observer.complete(); } error(error) { if (this._observer === undefined) { throw new Errors_1.ObserverError(); } if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; this._scope = new Scope_1.Scope({ errorCallback: (error) => { this._observer?.error(error); } }); this._scope.launch(this._transformerFactory(error, this._observer)); } } class TransformLatestFlow extends Flow { constructor(_flow, _transformerFactory) { super(); this._flow = _flow; this._transformerFactory = _transformerFactory; this._hasCompleted = false; this._scope = new Scope_1.Scope({ errorCallback: (error) => { this._observer.error(error); } }); } addObserver(observer) { if (this._observer !== undefined) { throw new Errors_1.FlowConsumedError(); } this._observer = observer; const downstreamObserver = new ObserverFunction_1.ObserverFunction((value) => { observer.emit(value); }, () => { if (this._hasCompleted) { observer.complete(); } }, (error) => { this._scope._cancelWithError(error); }); const upstreamObserver = new ObserverFunction_1.ObserverFunction((value) => { if (this._coroutine !== undefined) { this._scope._cancelCallbacks.get(this._coroutine)?.call(undefined); } this._coroutine = this._transformerFactory(value, downstreamObserver).call(this._scope); this._scope._resume(this._coroutine, { value: undefined }); }, () => { this._hasCompleted = true; }, (error) => { this._scope._cancelWithError(error); }); this._cancel = () => { this._flow.removeObserver(upstreamObserver); if (this._coroutine !== undefined) { this._scope._cancelCallbacks.get(this._coroutine)?.call(undefined); } }; this._flow.addObserver(upstreamObserver); } removeObserver(observer) { if (this._observer === observer) { throw new Errors_1.FlowRemoveObserverError(); } this._scope.cancel(); } } class FlowOf extends Flow { constructor(_coroutineFactory) { super(); this._coroutineFactory = _coroutineFactory; this._scope = new Scope_1.Scope({ errorCallback: (error) => { this._observer.error(error); } }); } addObserver(observer) { if (this._observer !== undefined) { throw new Errors_1.FlowConsumedError(); } this._observer = observer; const that = this; this._cancel = this._scope.launch(function* () { yield* this.call(that._coroutineFactory(observer)); observer.complete(); }); } removeObserver(observer) { if (this._observer !== observer) { throw new Errors_1.FlowRemoveObserverError(); } this._cancel(); } } const flowOf = (factory) => new FlowOf(factory); exports.flowOf = flowOf; class FlowOfValues extends Flow { constructor(...args) { super(); this._values = args; } addObserver(observer) { for (const value of this._values) { observer.emit(value); } observer.complete(); } removeObserver() { } } const flowOfValues = (...args) => new FlowOfValues(...args); exports.flowOfValues = flowOfValues; /** * Starts observing a upstream flow and shares received values to downstream observers. * SharedStateFlow replay the last emitted value to new observers. */ class SharedStateFlow extends Flow { constructor(_flow) { super(); this._flow = _flow; this._observers = new Set(); this._hasCompleted = false; this._flow.addObserver(this); } addObserver(observer) { this._observers.add(observer); if (this._last !== undefined) { observer.emit(this._last.value); } if (this._hasCompleted) { observer.complete(); } } removeObserver(observer) { if (!this._observers.has(observer)) { throw new Errors_1.FlowRemoveObserverError(); } this._observers.delete(observer); } emit(value) { if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._last = { value }; for (const observer of this._observers) { observer.emit(value); } } complete() { if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; for (const observer of this._observers) { observer.complete(); } } error(error) { for (const observer of this._observers) { observer?.error(error); } } } exports.SharedStateFlow = SharedStateFlow; /** * EventSubjects update their observers when there is a new event. Previously emitted values are not * replayed on new observers. To replay the last emitted value, use StateSubject. Subjects are hot * and can be shared with multipler observers. New flows that observe subjects start cold. */ class EventSubject extends Flow { constructor() { super(...arguments); this._observers = new Set(); } addObserver(observer) { this._observers.add(observer); } removeObserver(observer) { if (!this._observers.has(observer)) { throw new Errors_1.FlowRemoveObserverError(); } this._observers.delete(observer); } /** * Emits a value to the observer. * @param value */ emit(value) { for (const observer of this._observers) { observer.emit(value); } } } exports.EventSubject = EventSubject; /** * StateSubject always have a value. When new observers are added, the last emitted value is * replayed. This is generally used used for hot observables like the mouse position. Subjects are * hot and can be shared with multipler observers. New flows that observe subjects start cold. */ class StateSubject extends Flow { constructor(value) { super(); this.value = value; this._observers = new Set(); } addObserver(observer) { this._observers.add(observer); observer.emit(this.value); } removeObserver(observer) { if (!this._observers.has(observer)) { throw new Errors_1.FlowRemoveObserverError(); } this._observers.delete(observer); } emit(value) { this.value = value; for (const observer of this._observers) { observer.emit(value); } } get() { return this.value; } } exports.StateSubject = StateSubject; /** * Starts observing a upstream flow and shares received values to downstream observers. * SharedEventFlow doesn't replay any past emitted values. */ class SharedEventFlow extends Flow { constructor(_flow) { super(); this._flow = _flow; this._observers = new Set(); this._hasCompleted = false; this._flow.addObserver(this); } addObserver(observer) { this._observers.add(observer); } removeObserver(observer) { if (!this._observers.has(observer)) { throw new Errors_1.FlowRemoveObserverError(); } this._observers.delete(observer); } emit(value) { if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } for (const observer of this._observers) { observer.emit(value); } } complete() { if (this._hasCompleted) { throw new Errors_1.HasCompletedError(); } this._hasCompleted = true; for (const observer of this._observers) { observer.complete(); } } error(error) { for (const observer of this._observers) { observer?.error(error); } } } exports.SharedEventFlow = SharedEventFlow; //# sourceMappingURL=Flow.js.map