UNPKG

@fbltd/async

Version:

Miscellaneous async utils

122 lines (121 loc) 3.48 kB
import { PromiseConfiguration } from "../promise-configuration.js"; import { baseComparer } from "./utils.js"; import { symAI } from "../constants.js"; import { observationState } from "./observe.state.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 || this.abortPromise.isFulfilled; } [symAI](thisStreamConfig = {}) { const externalPromises = []; let firstPromise; const withReactionOnSubscribe = this.config.withReactionOnSubscribe || thisStreamConfig.withReactionOnSubscribe; if (withReactionOnSubscribe) { firstPromise = Promise.resolve(); externalPromises.push(firstPromise); } const owner = this; return { owner, dispose: owner.dispose.bind(owner), get isDisposed() { return owner.done; }, next: async () => { await Promise.race([ owner.next(), ...externalPromises, ]); if (firstPromise) { firstPromise = undefined; externalPromises.pop(); } if (this.done) { return { done: true }; } 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() { const done = { done: true }; const owner = this; if (this.done) return done; await this.getOrCreateRace(); this._race = undefined; if (this.done) return done; this.abortPromise = new PromiseConfiguration(); return { done: false, get value() { return owner.value; } }; } get disposePromise() { return this.abortPromise?.promise ?? Promise.resolve(); } dispose() { this.abortPromise?.resolve(); this.abortPromise = this.reactionPromise = undefined; } }