| 1 | export interface WorkspaceTreePersistenceClock { |
| 2 | requestFrame(callback: () => void): number; |
| 3 | cancelFrame(handle: number): void; |
| 4 | setTimer(callback: () => void, delayMs: number): ReturnType<typeof setTimeout>; |
| 5 | clearTimer(handle: ReturnType<typeof setTimeout>): void; |
| 6 | } |
| 7 | |
| 8 | export interface WorkspaceTreePersistenceScheduler { |
| 9 | schedule(memoryKey: string): void; |
| 10 | flush(): void; |
| 11 | cancel(): void; |
| 12 | } |
| 13 | |
| 14 | const browserClock: WorkspaceTreePersistenceClock = { |
| 15 | requestFrame(callback) { |
| 16 | if (typeof requestAnimationFrame === "function") return requestAnimationFrame(callback); |
| 17 | return setTimeout(callback, 0) as unknown as number; |
| 18 | }, |
| 19 | cancelFrame(handle) { |
| 20 | if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(handle); |
| 21 | else clearTimeout(handle); |
| 22 | }, |
| 23 | setTimer(callback, delayMs) { |
| 24 | return setTimeout(callback, delayMs); |
| 25 | }, |
| 26 | clearTimer(handle) { |
| 27 | clearTimeout(handle); |
| 28 | }, |
| 29 | }; |
| 30 | |
| 31 | export function createWorkspaceTreePersistenceScheduler( |
| 32 | persist: (memoryKey: string) => void, |
| 33 | delayMs = 200, |
| 34 | clock: WorkspaceTreePersistenceClock = browserClock, |
| 35 | ): WorkspaceTreePersistenceScheduler { |
| 36 | let frameHandle: number | null = null; |
| 37 | let timerHandle: ReturnType<typeof setTimeout> | null = null; |
| 38 | let pendingKey = ""; |
| 39 | let dirty = false; |
| 40 | |
| 41 | const clearPendingHandles = () => { |
| 42 | if (frameHandle != null) { |
| 43 | clock.cancelFrame(frameHandle); |
| 44 | frameHandle = null; |
| 45 | } |
| 46 | if (timerHandle != null) { |
| 47 | clock.clearTimer(timerHandle); |
| 48 | timerHandle = null; |
| 49 | } |
| 50 | }; |
| 51 | |
| 52 | const write = () => { |
| 53 | timerHandle = null; |
| 54 | if (!dirty || !pendingKey) return; |
| 55 | dirty = false; |
| 56 | persist(pendingKey); |
| 57 | }; |
| 58 | |
| 59 | return { |
| 60 | schedule(memoryKey) { |
| 61 | pendingKey = memoryKey; |
| 62 | dirty = true; |
| 63 | if (frameHandle != null) return; |
| 64 | frameHandle = clock.requestFrame(() => { |
| 65 | frameHandle = null; |
| 66 | if (timerHandle != null) clock.clearTimer(timerHandle); |
| 67 | timerHandle = clock.setTimer(write, delayMs); |
| 68 | }); |
| 69 | }, |
| 70 | flush() { |
| 71 | clearPendingHandles(); |
| 72 | write(); |
| 73 | }, |
| 74 | cancel() { |
| 75 | clearPendingHandles(); |
| 76 | dirty = false; |
| 77 | pendingKey = ""; |
| 78 | }, |
| 79 | }; |
| 80 | } |
| 81 |