import { Disposable } from './disposable' import { ValueObserver } from './value-observer' /** * Callback type for observable value changes */ export type ValueChangeCallback = (next: T) => void /** * Defines an ObservableValue value object. * * You can set and get its value with it's *setValue()* and *getValue()* methods and you can subscribe to value changes with *subscribe()* * * Usage example: * ```ts * const observableValue = new ObservableValue(0); * const observer = observableValue.subscribe((newValue) => { * console.log("Value changed:", newValue); * }); * // To update the value * observableValue.setValue(Math.random()); * // if you want to dispose a single observer * observer.dispose(); * // if you want to dispose the whole observableValue with all of its observers: * observableValue.dispose(); * ``` * * @param T Generic argument to indicate the value type */ export class ObservableValue implements Disposable { /** * Disposes the ObservableValue object, removes all observers */ public dispose() { this.observers.clear() } private observers: Set> = new Set() private currentValue!: T /** * Subscribes to a value changes * @param {ValueChangeCallback} callback The callback method that will be called on each change * @param {boolean} getLast Will call the callback with the last known value right after subscription * @returns {ValueObserver} The ValueObserver instance */ public subscribe(callback: ValueChangeCallback, getLast = false): ValueObserver { const observer = new ValueObserver(this, callback) this.observers.add(observer) if (getLast) { callback(this.currentValue) } return observer } /** * The observer will unsubscribe from the Observable * @param {ValueObserver} observer The ValueObserver instance * @returns if unsubscribing was successfull */ public unsubscribe(observer: ValueObserver): boolean { return this.observers.delete(observer) } /** * Gets the current Value * @returns {T} The current value */ public getValue(): T { return this.currentValue } /** * Sets a new value and notifies the observers. * @param {T} newValue The new value to be set */ public setValue(newValue: T) { if (this.currentValue !== newValue) { this.currentValue = newValue for (const subscription of this.observers) { subscription.callback(newValue) } } } /** * Gets the observers * @returns {ReadonlyArray>} The subscribed observers */ public getObservers(): ReadonlyArray> { return Array.from(this.observers) } /** * @constructs The ObservableValue object * @param {T} initialValue Optional initial value */ constructor(initialValue?: T) { if (initialValue) { this.currentValue = initialValue } } }