UNPKG

@fbltd/async

Version:

Miscellaneous async utils

125 lines (124 loc) 3.59 kB
import { PromiseConfiguration } from "../promise-configuration.js"; import { baseComparer } from "./utils.js"; import { observationState } from "./observe.state.js"; import { symAI } from "../constants.js"; export class Dependency { _value; reactionPromise; abortPromise = new PromiseConfiguration(); config; constructor(_value, config = {}) { this._value = _value; this.config = { withCustomEquality: baseComparer, withReactionOnSubscribe: false, ...config, }; } _set(v) { if (this.done) return; if (this.config.withCustomEquality(this._value, v)) { return; } this._value = v; if (this.reactionPromise) { this.reactionPromise.resolve(v); this.reactionPromise = undefined; } } set value(v) { this._set(v); } get value() { observationState.setDep(this); return this._value; } get value_unsafe() { return this._value; } get done() { return this.abortPromise.isFulfilled; } [symAI](thisStreamConfig = {}) { const externalPromises = []; let firstPromise; const withReactionOnSubscribe = this.config.withReactionOnSubscribe || thisStreamConfig.withReactionOnSubscribe; if (withReactionOnSubscribe) { firstPromise = new PromiseConfiguration(); firstPromise.resolve(); externalPromises.push(firstPromise.promise); } if (thisStreamConfig.externalDispose) { externalPromises.push(thisStreamConfig.externalDispose.promise); } const owner = this; let done = false; return { owner, dispose: owner.dispose.bind(owner), get isDisposed() { return done || owner.done; }, next: async () => { await Promise.race([ ...externalPromises, owner.next(), ]); if (this.done || thisStreamConfig.externalDispose?.isFulfilled) { done = true; return { done: true }; } if (firstPromise) { firstPromise = undefined; externalPromises.pop(); } return { done: false, get value() { return owner.value; } }; } }; } /** * One race of value change and dependency dispose * for all subscribers */ _race; getOrCreateRace() { if (!this._race) { this.reactionPromise = this.reactionPromise ?? new PromiseConfiguration(); this._race = Promise.race([ this.abortPromise.promise, this.reactionPromise.promise, ]); } return this._race; } /** * Another subscribe for current race */ async next() { let race = this.getOrCreateRace(); await race; this._race = undefined; if (this.done) { return { done: true }; } return { done: false, get value() { return this.value; } }; } get disposePromise() { return this.abortPromise.promise; } dispose() { this.abortPromise.resolve(); this.reactionPromise = undefined; } }