rdesign/frontend/node_modules/signal-utils/dist/subtle/batched-effect.ts.js

98 lines
2.7 KiB
JavaScript

import { Signal } from 'signal-polyfill';
const notifiedEffects = new Set();
let batchDepth = 0;
/**
* Runs the given function inside of a "batch" and calls any batched effects
* (those created with `batchEffect()`) that depend on updated signals
* synchronously after the function completes.
*
* Batches can be nested, and effects will only be called once at the end of the
* outermost batch.
*
* Batching does not change how the signal graph updates, or change any other
* watcher or effect system. Accessing signals that are updated within a batch
* will return their updates value. Other computations, watcher, and effects
* created outside of a batch that depend on updated signals will be run as
* usual.
*
* @param fn The function to run inside the batch.
*/
const batch = fn => {
batchDepth++;
try {
// Run the function to notifiy watchers
fn();
} finally {
batchDepth--;
if (batchDepth !== 0) {
return;
}
// Copy then clear the notified effects
const effects = [...notifiedEffects];
notifiedEffects.clear();
// Run all the batched effect callbacks and re-enable the watchers
let exceptions;
for (const {
computed,
watcher
} of effects) {
watcher.watch(computed);
try {
computed.get();
} catch (e) {
(exceptions ??= []).push(e);
}
}
if (exceptions !== undefined) {
if (exceptions.length === 1) {
throw exceptions[0];
} else {
throw new AggregateError(exceptions, "Multiple exceptions thrown in batched effects");
}
}
}
};
/**
* Creates an effect that runs synchronously at the end of a `batch()` call if
* any of the signals it depends on have been updated.
*
* The effect also runs asynchronously, on the microtask queue, if any of the
* signals it depends on have been updated outside of a `batch()` call.
*
* @param effectFn The function to run as an effect.
* @returns A function that stops and disposes the effect.
*/
const batchedEffect = effectFn => {
const computed = new Signal.Computed(effectFn);
const watcher = new Signal.subtle.Watcher(async () => {
// Synchonously add the effect to the notified effects
notifiedEffects.add(entry);
// Check if our effect is still in the notified effects
await 0;
if (notifiedEffects.has(entry)) {
// If it is, then we call it async and remove it
notifiedEffects.delete(entry);
computed.get();
}
});
const entry = {
computed,
watcher
};
watcher.watch(computed);
computed.get();
return () => {
watcher.unwatch(computed);
notifiedEffects.delete(entry);
};
};
export { batch, batchedEffect };
//# sourceMappingURL=batched-effect.ts.js.map