Docs: trim arch 05 debounced-renderer sketch to its generation-guard insight

This commit is contained in:
2026-06-15 00:51:30 +03:00
parent 66c123e15e
commit 713f396c5c
@@ -384,49 +384,23 @@ export interface DebouncedRenderer {
cancel(): void;
}
export function createDebouncedRenderer(opts: {
/** Current debounce delay in ms; read fresh each schedule so settings apply live. */
delayMs: () => number;
/** Performs one render. Reads the current spec/theme; awaits the embed. */
render: () => Promise<void>;
/** Toggle the non-blocking busy indicator. */
setBusy: (busy: boolean) => void;
}): DebouncedRenderer {
let timer: ReturnType<typeof setTimeout> | null = null;
let generation = 0; // guards against a stale in-flight render finishing late
// The factory wires the three operations over one timer. schedule() does
// `setTimeout(run, delayMs())`, clearing any pending timer first (delayMs read fresh
// so a settings change applies live); flush() clears the timer and runs now; cancel()
// clears it and bumps `generation`. The non-obvious part is out-of-order protection:
function createDebouncedRenderer(opts): DebouncedRenderer {
let generation = 0;
const run = async () => {
timer = null;
const mine = ++generation;
const mine = ++generation; // capture this render's turn
opts.setBusy(true);
try {
await opts.render();
} finally {
// Only the most recent render clears the indicator.
if (mine === generation) opts.setBusy(false);
if (mine === generation) opts.setBusy(false); // only the latest render clears it
}
};
return {
schedule() {
if (timer) clearTimeout(timer); // cancel the pending render
timer = setTimeout(run, opts.delayMs());
},
flush() {
if (timer) {
clearTimeout(timer);
timer = null;
}
void run();
},
cancel() {
if (timer) {
clearTimeout(timer);
timer = null;
}
generation++; // abandon any in-flight result
},
};
// …schedule/flush/cancel as above. A slow render that resolves after a newer one
// fails the `mine === generation` check, so it can't clobber the fresh view/indicator.
}
```