| 1 | export interface WorkspaceRefreshTimer { |
| 2 | schedule(callback: () => void, delayMs: number): unknown; |
| 3 | cancel(handle: unknown): void; |
| 4 | } |
| 5 | |
| 6 | export interface WorkspaceRefreshScheduler { |
| 7 | trigger(run: () => Promise<void> | void): void; |
| 8 | cancel(): void; |
| 9 | } |
| 10 | |
| 11 | const browserTimer: WorkspaceRefreshTimer = { |
| 12 | schedule: (callback, delayMs) => window.setTimeout(callback, delayMs), |
| 13 | cancel: (handle) => window.clearTimeout(handle as number), |
| 14 | }; |
| 15 | |
| 16 | /** |
| 17 | * Creates a trailing quiet-window scheduler with at most one refresh in flight. |
| 18 | * A trigger while a refresh is running is retained and executes once the quiet |
| 19 | * window has elapsed and the prior refresh settles. |
| 20 | */ |
| 21 | export function createWorkspaceRefreshScheduler(delayMs: number, timer: WorkspaceRefreshTimer = browserTimer): WorkspaceRefreshScheduler { |
| 22 | let handle: unknown = null; |
| 23 | let inFlight = false; |
| 24 | let trailing = false; |
| 25 | let latest: (() => Promise<void> | void) | null = null; |
| 26 | let cancelled = false; |
| 27 | |
| 28 | const start = () => { |
| 29 | const run = latest; |
| 30 | if (!run || cancelled) return; |
| 31 | if (inFlight) { |
| 32 | trailing = true; |
| 33 | return; |
| 34 | } |
| 35 | inFlight = true; |
| 36 | void Promise.resolve().then(run).finally(() => { |
| 37 | inFlight = false; |
| 38 | if (cancelled || !trailing) return; |
| 39 | trailing = false; |
| 40 | start(); |
| 41 | }); |
| 42 | }; |
| 43 | |
| 44 | return { |
| 45 | trigger(run: () => Promise<void> | void): void { |
| 46 | cancelled = false; |
| 47 | latest = run; |
| 48 | trailing = false; |
| 49 | if (handle !== null) timer.cancel(handle); |
| 50 | handle = timer.schedule(() => { |
| 51 | handle = null; |
| 52 | start(); |
| 53 | }, delayMs); |
| 54 | }, |
| 55 | cancel(): void { |
| 56 | cancelled = true; |
| 57 | trailing = false; |
| 58 | latest = null; |
| 59 | if (handle !== null) timer.cancel(handle); |
| 60 | handle = null; |
| 61 | }, |
| 62 | }; |
| 63 | } |
| 64 |