@fbltd/async
Version:
Miscellaneous async utils
125 lines (124 loc) • 3.59 kB
JavaScript
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;
}
}