From 33330d9d31836ec2918561f63b9fd9222f3e377c Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Wed, 21 Oct 2026 19:10:06 +0800 Subject: [PATCH 1/3] fix: correct debounce remaining wait after repeated calls --- packages/vtable/__tests__/debounce.test.ts | 35 ++++++++++++++++++++++ packages/vtable/src/tools/debounce.ts | 3 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 packages/vtable/__tests__/debounce.test.ts diff --git a/packages/vtable/__tests__/debounce.test.ts b/packages/vtable/__tests__/debounce.test.ts new file mode 100644 index 0000000000..a34414300e --- /dev/null +++ b/packages/vtable/__tests__/debounce.test.ts @@ -0,0 +1,35 @@ +import { debounce } from '../src/tools/debounce'; + +jest.mock('@src/vrender', () => ({ vglobal: {} })); + +describe('debounce', () => { + test('waits for the remaining delay after a call during the debounce window', () => { + let now = 1000; + const timers: { callback: () => void; delay: number }[] = []; + const callback = jest.fn(); + jest.spyOn(Date, 'now').mockImplementation(() => now); + jest.spyOn(globalThis, 'setTimeout').mockImplementation((handler, delay) => { + timers.push({ callback: handler as () => void, delay: delay as number }); + return timers.length as any; + }); + + try { + const debounced = debounce(callback, 100); + debounced('first'); + now = 1050; + debounced('latest'); + + now = 1100; + timers.shift()!.callback(); + expect(callback).not.toHaveBeenCalled(); + expect(timers[0].delay).toBe(50); + + now = 1150; + timers.shift()!.callback(); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith('latest'); + } finally { + jest.restoreAllMocks(); + } + }); +}); diff --git a/packages/vtable/src/tools/debounce.ts b/packages/vtable/src/tools/debounce.ts index 6a6eee01a6..d684ff1b60 100644 --- a/packages/vtable/src/tools/debounce.ts +++ b/packages/vtable/src/tools/debounce.ts @@ -88,13 +88,14 @@ export function debounce(func: Function, wait?: number, options?: any) { return result; } + // 计算尾调用前的剩余等待时间,避免连续调用时反复创建零延迟定时器。 function remainingWait(time: number) { // 计算 time 与最近一次调用 debounced 函数的时间差 const timeSinceLastCall = time - lastCallTime; // 计算 time 与最近一次调用 func 函数的时间差 const timeSinceLastInvoke = time - lastInvokeTime; // 用 wait 减去已经等待的时间 - const timeWaiting = wait && -timeSinceLastCall; + const timeWaiting = (wait as number) - timeSinceLastCall; return maxing ? Math.min(timeWaiting as number, maxWait - timeSinceLastInvoke) : timeWaiting; } From d1db24285fbc10e19e357135787c0e7c0934ed9d Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Thu, 22 Oct 2026 13:55:32 +0800 Subject: [PATCH 2/3] test: cover final millisecond of debounce delay --- packages/vtable/__tests__/debounce.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/vtable/__tests__/debounce.test.ts b/packages/vtable/__tests__/debounce.test.ts index a34414300e..83a4870632 100644 --- a/packages/vtable/__tests__/debounce.test.ts +++ b/packages/vtable/__tests__/debounce.test.ts @@ -24,6 +24,12 @@ describe('debounce', () => { expect(callback).not.toHaveBeenCalled(); expect(timers[0].delay).toBe(50); + // 提前触发时仍等待最后 1ms,避免过早调用用户回调。 + now = 1149; + timers.shift()!.callback(); + expect(callback).not.toHaveBeenCalled(); + expect(timers[0].delay).toBe(1); + now = 1150; timers.shift()!.callback(); expect(callback).toHaveBeenCalledTimes(1); From 7a577048996c0b1195a03e9fe67568f398ba59f9 Mon Sep 17 00:00:00 2001 From: Rui-Sun Date: Thu, 22 Oct 2026 21:18:45 +0800 Subject: [PATCH 3/3] test: refresh debounce regression verification