UNPKG

@discoveryjs/discovery

Version:

Frontend framework for rapid data (JSON) analysis, shareable serverless reports and dashboards

75 lines (74 loc) 2.05 kB
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; } }