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 101 102 103 104 105 106 107 108 109 110 111 | 18x 62x 136x 136x 134x 130x 130x 134x 62x 46x 46x 46x 46x 62x 16x 16x 16x 16x 16x 16x 2x 2x 2x 62x 108x 9x 9x 9x 107x 1x 106x 106x 106x 106x 106x 5x 5x 5x 106x 2x 106x 106x 106x 62x 62x 62x | import { batch, batchFlush, batchStart, batchTasks } from "./batch";
import { type Disposer, type Get, type Readable, type ReadableLike } from "./interface";
import { getReadable, unsubscribe } from "./utils";
export interface WatchEffect {
(get: Get, dispose: Disposer): (() => void) | undefined | void;
}
/**
* Watch a reactive effect and re-run it when its dependencies change.
*
* @category SideEffects
* @param effect - The reactive effect to watch.
* @returns A disposer function to stop watching the effect.
*
* @example
* ```ts
* import { watch, writable, reactiveMap } from "./watch";
*
* const count$ = writable(0);
* const map$ = reactiveMap();
*
* watch((get) => {
* console.log(get(count$));
* console.log(get(map$).get("key"));
* });
* ```
*/
export const watch = (effect: WatchEffect): Disposer => {
let running: boolean | undefined;
let disposed: boolean | undefined;
let collectedDeps: Set<Readable> | undefined;
let cleanupEffect: Disposer | null | undefined | void;
const get: Get = ($?: ReadableLike): unknown => {
const readable = getReadable($);
if (!readable) return $;
if (!collectedDeps?.has(readable)) {
(collectedDeps ??= new Set()).add(readable);
readable.onReaction_(subscription);
}
return readable.get();
};
const subscription = () => {
/** c8 ignore else -- @preserve */
if (!running) {
unsubscribe(collectedDeps, subscription);
collectedDeps?.clear();
}
batchTasks.add(runner);
};
const dispose = () => {
disposed = true;
batchTasks.delete(runner);
effect = null as any; // enable gc
unsubscribe(collectedDeps, subscription);
collectedDeps = undefined;
if (cleanupEffect) {
const cleanup = cleanupEffect;
cleanupEffect = null;
batch(cleanup);
}
};
const runner = () => {
if (cleanupEffect) {
const cleanup = cleanupEffect;
cleanupEffect = null;
batch(cleanup);
}
if (disposed) {
return;
}
const isTopRunner = !running;
running = true;
const isBatchTop = batchStart();
try {
cleanupEffect = effect(get, dispose);
} catch (e) {
unsubscribe(collectedDeps, subscription);
collectedDeps?.clear();
throw e;
} finally {
if (disposed) {
dispose();
}
/** c8 ignore else -- @preserve */
if (isTopRunner) {
running = false;
}
isBatchTop && batchFlush();
}
};
runner.batchTask_ = runner;
runner();
return dispose;
};
|