@towns-protocol/sdk
Version:
For more details, visit the following resources:
109 lines • 3.51 kB
JavaScript
export class Observable {
_nextId = 0;
subscribers = [];
_value;
_dispose;
constructor(value) {
this._value = value;
}
get value() {
return this._value;
}
set(fn) {
return this.setValue(fn(this.value));
}
setValue(newValue) {
if (this._value === newValue) {
return false;
}
const prevValue = this._value;
this._value = newValue;
this.notify(prevValue);
return true;
}
subscribe(subscriber, opts = {}) {
const nextId = this._nextId++;
const sub = {
id: nextId,
fn: subscriber,
once: opts?.once ?? false,
condition: opts?.condition ?? (() => true),
};
this.subscribers.push(sub);
if (opts.fireImediately) {
this._notify(sub, this.value, this.value);
}
return () => this.unsubscribe(subscriber);
}
when(condition, opts = { timeoutMs: 5000 }) {
const logId = opts.description ? ` ${opts.description}` : '';
const timeoutError = new Error(`Timeout waiting for condition${logId}`);
return new Promise((resolve, reject) => {
const timeoutHandle = setTimeout(() => {
reject(timeoutError);
}, opts.timeoutMs);
this.subscribe((value) => {
clearTimeout(timeoutHandle);
resolve(value);
}, { fireImediately: true, condition: condition, once: true });
});
}
unsubscribe(subscriber) {
this.subscribers = this.subscribers.filter((sub) => {
if (sub.fn === subscriber) {
return false;
}
return true;
});
}
// T is the observable’s element type, U is the mapped element type
map(fn) {
const mappedObservable = new Observable(fn(this.value, this.value));
mappedObservable._dispose = this.subscribe((newValue, prevValue) => {
mappedObservable.setValue(fn(newValue, prevValue, mappedObservable.value));
});
return mappedObservable;
}
throttle(ms) {
const throttledObservable = new Observable(this.value);
let timeoutId = null;
let pendingValue = null;
const unsubscriber = this.subscribe((newValue) => {
pendingValue = newValue;
if (timeoutId === null) {
timeoutId = setTimeout(() => {
if (pendingValue !== null) {
throttledObservable.setValue(pendingValue);
pendingValue = null;
}
timeoutId = null;
}, ms);
}
});
throttledObservable._dispose = () => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
unsubscriber();
};
return throttledObservable;
}
dispose() {
this.subscribers = [];
this._dispose?.();
}
notify(prevValue) {
const subscriptions = this.subscribers;
subscriptions.forEach((sub) => this._notify(sub, this.value, prevValue));
}
_notify(sub, value, prevValue) {
if (sub.condition(value)) {
sub.fn(value, prevValue);
if (sub.once) {
this.subscribers = this.subscribers.filter((s) => s !== sub);
}
}
}
}
//# sourceMappingURL=observable.js.map