Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | 1x 1x 1x 1x 38x 38x 43x 38x 27x 1x 26x 26x 1x 25x 25x 25x 25x 25x 25x 25x 25x 25x 31x 31x 21x 21x 31x 43x 43x 5x 51x 31x 2x 2x 1x 27x 1x 14x 1x | import {FanOut} from './fanout';
const NO_CACHE: unique symbol = Symbol();
/** React.js synchronous state interface. */
export interface SyncStore<T> {
subscribe: SyncStoreSubscribe;
getSnapshot: () => T;
}
export type SyncStoreSubscribe = (callback: () => void) => SyncStoreUnsubscribe;
export type SyncStoreUnsubscribe = () => void;
export type SyncDep<T> = SyncValue<T> & SyncStore<T> & FanOut<void>;
export type WrapListInSyncDep<T extends unknown[]> = {
[K in keyof T]: SyncDep<T[K]>;
};
export interface Disposable {
dispose(): void;
}
export interface SyncValue<T> {
value: T;
}
export class Value<V> extends FanOut<void> implements SyncStore<V>, SyncValue<V> {
constructor(value: V) {
super();
this.value = value;
}
public next(value: V, force = false): void {
if (!force && this.value === value) return;
this.value = value;
this.emit();
}
/** ----------------------------------------------------- {@link SyncValue} */
public value: V;
/** ----------------------------------------------------- {@link SyncStore} */
public readonly subscribe: SyncStoreSubscribe = (cb) => this.listen(cb);
public readonly getSnapshot: () => V = () => this.value;
}
export class Computed<N, V extends unknown[] = any>
extends FanOut<void>
implements SyncValue<N>, SyncStore<N>, Disposable
{
private cache: N | typeof NO_CACHE = NO_CACHE;
private subs: SyncStoreUnsubscribe[];
constructor(
protected readonly deps: WrapListInSyncDep<V>,
protected readonly compute: (args: V) => N,
) {
super();
const subs = (this.subs = [] as SyncStoreUnsubscribe[]);
const length = deps.length;
for (let i = 0; i < length; i++) {
const dep = deps[i];
const sub = dep.listen(() => {
this.cache = NO_CACHE;
this.emit();
});
subs.push(sub);
}
}
private _comp(): N {
const cached = this.cache;
if (cached !== NO_CACHE) return cached;
return (this.cache = this.compute(this.deps.map((dep) => dep.getSnapshot()) as V));
}
/** ----------------------------------------------------- {@link SyncValue} */
public get value(): N {
return this._comp();
}
/** ----------------------------------------------------- {@link SyncStore} */
public readonly subscribe: SyncStoreSubscribe = (cb) => this.listen(cb);
public readonly getSnapshot: () => N = () => this._comp();
/** ---------------------------------------------------- {@link Disposable} */
public dispose() {
for (const sub of this.subs) sub();
}
}
export const val = <V>(initial: V): Value<V> => new Value(initial);
export const comp = <V extends unknown[], N>(deps: WrapListInSyncDep<V>, compute: (args: V) => N): Computed<N, V> =>
new Computed(deps, compute);
|