Skip to content

Commit

Permalink
correct leading debounce
Browse files Browse the repository at this point in the history
  • Loading branch information
KingSora committed May 23, 2024
1 parent 774df57 commit dbd10ef
Show file tree
Hide file tree
Showing 3 changed files with 347 additions and 2 deletions.
Binary file removed packages/overlayscrollbars/dist.zip
Binary file not shown.
7 changes: 5 additions & 2 deletions packages/overlayscrollbars/src/support/utils/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const debounce = <FunctionToDebounce extends (...args: any) => any>(
const mergeParamsResult = mergeParms(args);
const invokedArgs = mergeParamsResult || args;
const boundInvoke = invokeFunctionToDebounce.bind(0, invokedArgs);
let timeoutId: number | undefined;

// if (!mergeParamsResult) {
// invokeFunctionToDebounce(prevArguments || args);
Expand All @@ -118,11 +119,13 @@ export const debounce = <FunctionToDebounce extends (...args: any) => any>(
if (_leading && !leadingInvoked) {
boundInvoke();
leadingInvoked = true;
// @ts-ignore
timeoutId = setTimeoutFn(() => (leadingInvoked = undefined), finalTimeout);
} else {
// @ts-ignore
const timeoutId = setTimeoutFn(boundInvoke, finalTimeout);
clear = () => clearTimeoutFn(timeoutId);
timeoutId = setTimeoutFn(boundInvoke, finalTimeout);
}
clear = () => clearTimeoutFn(timeoutId);

if (hasMaxWait && !maxTimeoutId) {
maxTimeoutId = setT(flush, finalMaxWait as number);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ describe('function', () => {

jest.advanceTimersByTime(1);
expect(i).toBe(1);

jest.advanceTimersByTime(20000);
expect(i).toBe(1);
});

test('with maxDelay and shorter timeout with multiple calls', async () => {
Expand Down Expand Up @@ -316,6 +319,345 @@ describe('function', () => {
});
});
});

describe('leading', () => {
describe('timeout', () => {
test('without timeout', () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{
_leading: true,
}
);
expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled();
debouncedFn();
expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled();
expect(i).toBe(1);
});

test('with timeout 0', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 0, _leading: true }
);

expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled();

debouncedFn();
expect(rAF).toHaveBeenCalledTimes(1);
expect(setT).not.toHaveBeenCalled();
expect(i).toBe(1);

jest.advanceTimersByTime(0);

expect(i).toBe(1);
expect(rAF).toHaveBeenCalledTimes(1);
expect(setT).not.toHaveBeenCalled();
});

test('with timeout > 0', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 100, _leading: true }
);

expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled();

debouncedFn();
expect(rAF).not.toHaveBeenCalled();
expect(setT).toHaveBeenCalledTimes(1);
expect(i).toBe(1);

jest.advanceTimersByTime(100);

expect(i).toBe(1);
expect(rAF).not.toHaveBeenCalled();
expect(setT).toHaveBeenCalledTimes(1);
});

test('with timeout > 0 and multiple calls', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 200, _leading: true }
);

debouncedFn();
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(100);
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(100);
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(100);
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(100);
expect(i).toBe(1);

debouncedFn();

jest.advanceTimersByTime(200);
expect(i).toBe(2);
jest.advanceTimersByTime(200);
expect(i).toBe(2);

debouncedFn();
expect(i).toBe(3);

jest.advanceTimersByTime(200);
expect(i).toBe(3);

debouncedFn();
expect(i).toBe(4);
jest.advanceTimersByTime(200);
expect(i).toBe(4);
jest.advanceTimersByTime(200);
expect(i).toBe(4);
});

test('with timeout function', async () => {
let i = 0;
let timeoutMs = 200;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: () => timeoutMs, _leading: true }
);

debouncedFn();
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(100);
expect(i).toBe(1);

timeoutMs = 1000;

debouncedFn();
jest.advanceTimersByTime(500);
expect(i).toBe(1);

jest.advanceTimersByTime(500);
expect(i).toBe(2);
});
});

describe('maxDelay', () => {
test('without maxDelay', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 100, _leading: true }
);
debouncedFn();
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(99);
expect(i).toBe(1);

jest.advanceTimersByTime(1);
expect(i).toBe(2);
});

test('with maxDelay and longer timeout', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 10000, _maxDelay: 100, _leading: true }
);
debouncedFn();
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(99);
expect(i).toBe(1);

jest.advanceTimersByTime(1);
expect(i).toBe(2);

jest.advanceTimersByTime(20000);
expect(i).toBe(2);
});

test('with maxDelay and shorter timeout with multiple calls', async () => {
let i = 0;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 200, _maxDelay: 500, _leading: true }
);
debouncedFn();
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(150); // 150
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(150); // 300

debouncedFn();
jest.advanceTimersByTime(150); // 450
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(49); // 499
expect(i).toBe(1);

jest.advanceTimersByTime(1); // 500
expect(i).toBe(2);
});

test('with maxDelay function', async () => {
let i = 0;
let maxDelayMs = 300;
const debouncedFn = debounce(
() => {
i += 1;
},
{ _timeout: 400, _maxDelay: () => maxDelayMs, _leading: true }
);
debouncedFn();
expect(i).toBe(1);

debouncedFn();
jest.advanceTimersByTime(100); // 100
expect(i).toBe(1);

debouncedFn();
expect(i).toBe(1);
jest.advanceTimersByTime(100); // 200

maxDelayMs = 800; // this delay will be applied in the next cycle, not instantly

debouncedFn();
expect(i).toBe(1);
jest.advanceTimersByTime(99); // 299
expect(i).toBe(1);
jest.advanceTimersByTime(1); // 300
expect(i).toBe(2);

debouncedFn();
expect(i).toBe(3);
jest.advanceTimersByTime(300); // 300

debouncedFn();
expect(i).toBe(3);
jest.advanceTimersByTime(300); // 600

debouncedFn();
expect(i).toBe(3);
jest.advanceTimersByTime(199); // 799
expect(i).toBe(3);
jest.advanceTimersByTime(1); // 800
expect(i).toBe(4); // max delay 800 invoked here

debouncedFn();
expect(i).toBe(5);
jest.advanceTimersByTime(399);
debouncedFn();
expect(i).toBe(5);
jest.advanceTimersByTime(401);
expect(i).toBe(6);
});
});

describe('mergeParams', () => {
test('with correct mergeParams function', async () => {
let i = 0;
const _mergeParams = jest.fn((prev: [number, number], curr: [number, number]) => {
const [prevA, prevB] = prev;
const [currA, currB] = curr;

return [prevA + currA, prevB + currB] as [number, number];
});
const debouncedFn = debounce(
(a: number, b: number) => {
i += a * b;
},
{ _timeout: 200, _mergeParams, _leading: true }
);
debouncedFn(1, 1);
expect(i).toBe(1);
expect(_mergeParams).not.toHaveBeenCalled();

jest.advanceTimersByTime(100); // 100

debouncedFn(4, 4);
expect(i).toBe(1);
expect(_mergeParams).toHaveBeenLastCalledWith([1, 1], [4, 4]);

jest.advanceTimersByTime(100); // 200

debouncedFn(10, 10);
expect(i).toBe(1);
expect(_mergeParams).toHaveBeenLastCalledWith([5, 5], [10, 10]);

jest.advanceTimersByTime(250); // 450

expect(i).toBe(15 * 15 + 1);
});

test('without correct mergeParams function', async () => {
let i = 0;
const _mergeParams = jest.fn(() => null);
const debouncedFn = debounce(
(a, b) => {
i += a * b;
},
{ _timeout: 200, _mergeParams, _leading: true }
);
debouncedFn(1, 1);
expect(i).toBe(1);
expect(_mergeParams).not.toHaveBeenCalled();

jest.advanceTimersByTime(100); // 100

debouncedFn(2, 2);
expect(i).toBe(1);
expect(_mergeParams).toHaveBeenLastCalledWith([1, 1], [2, 2]);

jest.advanceTimersByTime(100); // 200

debouncedFn(3, 3);
expect(i).toBe(1);
expect(_mergeParams).toHaveBeenLastCalledWith([2, 2], [3, 3]);

jest.advanceTimersByTime(250); // 450

expect(i).toBe(3 * 3 + 1);
});
});
});
});

test('bind', () => {
Expand Down

0 comments on commit dbd10ef

Please sign in to comment.