75 lines
1.6 KiB
TypeScript
75 lines
1.6 KiB
TypeScript
const contextStack: Observer[] = [];
|
|
|
|
export class Observer<T = unknown> {
|
|
private effect: () => T;
|
|
|
|
dependencies: Set<Observable> = new Set();
|
|
|
|
constructor(effect: { (): T }) {
|
|
this.effect = effect;
|
|
this.execute();
|
|
}
|
|
|
|
execute() {
|
|
this.cleanup();
|
|
contextStack.push(this);
|
|
try {
|
|
this.effect();
|
|
} finally {
|
|
contextStack.pop();
|
|
}
|
|
}
|
|
|
|
cleanup() {
|
|
for (const dep of this.dependencies.values()) {
|
|
dep.observers.delete(this);
|
|
}
|
|
this.dependencies.clear();
|
|
}
|
|
}
|
|
|
|
export class Observable<T = unknown> {
|
|
private value: T;
|
|
|
|
observers: Set<Observer> = new Set();
|
|
|
|
constructor(init: T) {
|
|
this.value = init;
|
|
}
|
|
|
|
get() {
|
|
const activeObserver = contextStack[contextStack.length - 1];
|
|
if (activeObserver) {
|
|
this.observers.add(activeObserver);
|
|
activeObserver.dependencies.add(this);
|
|
}
|
|
return this.value;
|
|
}
|
|
|
|
set(next: T) {
|
|
this.value = next;
|
|
for (const observer of Array.from(this.observers.values())) {
|
|
observer.execute();
|
|
}
|
|
}
|
|
}
|
|
|
|
export class Mapping<T extends unknown> {
|
|
private observable: Observable<T>;
|
|
|
|
private observer: Observer<T>;
|
|
|
|
constructor(mapping: { (): T }) {
|
|
this.observable = new Observable(undefined as T);
|
|
this.observer = new Observer(() => {
|
|
const result = mapping();
|
|
this.observable.set(result);
|
|
return result;
|
|
});
|
|
}
|
|
|
|
get() {
|
|
return this.observable.get();
|
|
}
|
|
}
|