mirror of
https://github.com/morten-olsen/morten-olsen.github.io.git
synced 2026-02-08 01:46:28 +01:00
92 lines
2.1 KiB
TypeScript
92 lines
2.1 KiB
TypeScript
type Observer = () => void;
|
|
|
|
type ObservableRecord<T extends Record<string, Observable<any>>> = {
|
|
[K in keyof T]: T[K] extends Observable<infer U> ? U : never;
|
|
};
|
|
|
|
class Observable<T> {
|
|
#observers: Observer[] = [];
|
|
#data?: Promise<T>;
|
|
#loader: (current?: T) => Promise<T>;
|
|
|
|
constructor(loader: () => Promise<T>) {
|
|
this.#loader = loader;
|
|
}
|
|
|
|
public get ready() {
|
|
return this.#data;
|
|
}
|
|
|
|
public get data() {
|
|
if (!this.#data) {
|
|
this.#data = this.#loader(this.#data);
|
|
}
|
|
return this.#data;
|
|
}
|
|
|
|
public recreate = () => {
|
|
this.#data = undefined;
|
|
this.notify();
|
|
};
|
|
|
|
public set(loader: (current?: T) => Promise<T>) {
|
|
this.#data = undefined;
|
|
this.#loader = loader;
|
|
this.notify();
|
|
}
|
|
|
|
public notify = () => {
|
|
this.#observers.forEach((observer) => observer());
|
|
};
|
|
|
|
subscribe = (observer: Observer) => {
|
|
this.#observers.push(observer);
|
|
return () => this.unsubscribe(observer);
|
|
};
|
|
|
|
unsubscribe = (observer: Observer) => {
|
|
this.#observers = this.#observers.filter((o) => o !== observer);
|
|
};
|
|
|
|
pipe = <U>(fn: (data: T) => Promise<U>) => {
|
|
const loader = async () => fn(await this.data);
|
|
const observable = new Observable<U>(loader);
|
|
this.subscribe(() => {
|
|
observable.set(loader);
|
|
});
|
|
return observable;
|
|
};
|
|
|
|
static combine = <U extends Record<string, Observable<any>>>(
|
|
record: U,
|
|
): Observable<ObservableRecord<U>> => {
|
|
const loader = () =>
|
|
Object.entries(record).reduce(
|
|
async (accP, [key, value]) => ({
|
|
...(await accP),
|
|
[key]: await value.data,
|
|
}),
|
|
{} as any,
|
|
);
|
|
const observable = new Observable<ObservableRecord<U>>(loader);
|
|
Object.values(record).forEach((item) => {
|
|
item.subscribe(async () => {
|
|
observable.set(loader);
|
|
});
|
|
});
|
|
return observable;
|
|
};
|
|
|
|
static link = <T>(observables: Observable<any>[], generate: () => Promise<T>) => {
|
|
const observable = new Observable<T>(generate);
|
|
observables.forEach((item) => {
|
|
item.subscribe(() => {
|
|
observable.recreate();
|
|
});
|
|
});
|
|
return observable;
|
|
};
|
|
}
|
|
|
|
export { Observable };
|