From 3f8e9a843348a5069f4253f8020415df3aa98cee Mon Sep 17 00:00:00 2001 From: lzt404 <2596933790@qq.com> Date: Fri, 18 Sep 2026 16:17:59 +0800 Subject: [PATCH] fix(usage): prioritize filter requests over background refreshes --- src/pages/UsageRecordsPage.tsx | 1 + src/services/refreshScheduler.ts | 19 ++++++++++++++++++- tests/refreshScheduler.test.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/pages/UsageRecordsPage.tsx b/src/pages/UsageRecordsPage.tsx index ed25b6a..8a424fd 100644 --- a/src/pages/UsageRecordsPage.tsx +++ b/src/pages/UsageRecordsPage.tsx @@ -404,6 +404,7 @@ export function UsageRecordsPage() { const loadData = useCallback( (quiet = false, immediate = !quiet) => { if (!quiet) setLoading(true); + if (!quiet) return schedulerRef.current!.runForeground(() => executeLoadData(false)); return schedulerRef.current!.schedule(() => executeLoadData(quiet), immediate); }, [executeLoadData], diff --git a/src/services/refreshScheduler.ts b/src/services/refreshScheduler.ts index d5dd239..4addd26 100644 --- a/src/services/refreshScheduler.ts +++ b/src/services/refreshScheduler.ts @@ -2,6 +2,7 @@ export function createRefreshScheduler(minIntervalMs = 1_000) { type Task = () => Promise; type Completion = { promise: Promise; resolve: () => void; reject: (error: unknown) => void }; let running = false; + let foregroundTasks = 0; let completedAt = -Infinity; let pending: Task | null = null; let urgent = false; @@ -9,7 +10,7 @@ export function createRefreshScheduler(minIntervalMs = 1_000) { let timer: ReturnType | null = null; const pump = () => { - if (running || !pending) return; + if (running || foregroundTasks > 0 || !pending) return; if (timer !== null) clearTimeout(timer); timer = null; const delay = urgent ? 0 : Math.max(0, minIntervalMs - (performance.now() - completedAt)); @@ -38,6 +39,22 @@ export function createRefreshScheduler(minIntervalMs = 1_000) { }; return { + async runForeground(task: Task): Promise { + if (timer !== null) clearTimeout(timer); + timer = null; + pending = null; + urgent = false; + completion?.resolve(); + completion = null; + foregroundTasks += 1; + try { + await task(); + } finally { + foregroundTasks -= 1; + completedAt = performance.now(); + pump(); + } + }, schedule(task: Task, immediate = false): Promise { pending = task; urgent ||= immediate; diff --git a/tests/refreshScheduler.test.ts b/tests/refreshScheduler.test.ts index 1f79ebc..95188dd 100644 --- a/tests/refreshScheduler.test.ts +++ b/tests/refreshScheduler.test.ts @@ -42,6 +42,32 @@ describe('refresh scheduler', () => { expect(performance.now() - started).toBeGreaterThanOrEqual(25); }); + it('starts foreground work immediately and holds trailing background refreshes until it finishes', async () => { + const scheduler = createRefreshScheduler(0); + const backgroundGate = deferred(); + const foregroundGate = deferred(); + const calls: string[] = []; + const background = scheduler.schedule(async () => { + calls.push('background'); + await backgroundGate.promise; + }); + const obsolete = scheduler.schedule(async () => { calls.push('obsolete'); }); + const foreground = scheduler.runForeground(async () => { + calls.push('foreground'); + await foregroundGate.promise; + }); + const trailing = scheduler.schedule(async () => { calls.push('trailing'); }, true); + + expect(calls).toEqual(['background', 'foreground']); + await obsolete; + backgroundGate.resolve(); + await background; + expect(calls).toEqual(['background', 'foreground']); + foregroundGate.resolve(); + await Promise.all([foreground, trailing]); + expect(calls).toEqual(['background', 'foreground', 'trailing']); + }); + it('cancels queued work during navigation and accepts a fresh request', async () => { const scheduler = createRefreshScheduler(0); const gate = deferred();