fix(usage): prioritize filter requests over background refreshes

This commit is contained in:
lzt404 committed 2026-09-18 18:17:31 +08:00
1 parent ba247159da
commit 3f8e9a8433
3 files changed
+45 -1

No files matched your search

+1
View File
@@ -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],
+18 -1
View File
@@ -2,6 +2,7 @@ export function createRefreshScheduler(minIntervalMs = 1_000) {
type Task = () => Promise<void>;
type Completion = { promise: Promise<void>; 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<typeof setTimeout> | 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<void> {
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<void> {
pending = task;
urgent ||= immediate;
+26
View File
@@ -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();