@discoveryjs/discovery
Version:
Frontend framework for rapid data (JSON) analysis, shareable serverless reports and dashboards
75 lines (74 loc) • 2.05 kB
JavaScript
export class Observer {
subscriber;
value;
constructor(initValue, shouldUpdate) {
this.subscriber = null;
this.value = initValue;
this.shouldUpdate = typeof shouldUpdate === "function" ? shouldUpdate : this.shouldUpdate;
}
get readonly() {
const host = this;
return {
// FIXME: TS should infer types for subscribe/subscribeSync/unsubscribe,
// however it doesn't and produces `any` instead. Used `as Observer<T>[method]`
// as a workaround.
subscribe: this.subscribe.bind(this),
subscribeSync: this.subscribeSync.bind(this),
unsubscribe: this.unsubscribe.bind(this),
get value() {
return host.value;
}
};
}
subscribe(callback) {
this.subscriber = {
callback,
subscriber: this.subscriber
};
return () => this.unsubscribe(callback);
}
subscribeSync(callback) {
const unsubscribe = this.subscribe(callback);
callback(this.value, unsubscribe);
return unsubscribe;
}
unsubscribe(callback) {
let prev = this;
let cursor = this.subscriber;
while (cursor !== null) {
if (cursor.callback === callback) {
cursor.callback = null;
prev.subscriber = cursor.subscriber;
break;
}
prev = cursor;
cursor = cursor.subscriber;
}
}
shouldUpdate(newValue, oldValue) {
return newValue !== oldValue;
}
set(value) {
return this.#setValue(value) !== false;
}
asyncSet(value) {
const callbacks = this.#setValue(value);
return callbacks === false ? Promise.resolve(false) : Promise.all(callbacks).then(() => true);
}
#setValue(value) {
if (!this.shouldUpdate(value, this.value)) {
return false;
}
const callbacks = [];
let cursor = this.subscriber;
this.value = value;
while (cursor !== null) {
const { callback } = cursor;
if (callback !== null) {
callbacks.push(callback(value, () => this.unsubscribe(callback)));
}
cursor = cursor.subscriber;
}
return callbacks;
}
}