diff --git a/index.d.ts b/index.d.ts index ba35a43..8edf0c3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,9 +3,13 @@ type AnyFunction = (...arguments_: readonly any[]) => unknown; /** Creates a debounced function that delays execution until `wait` milliseconds have passed since its last invocation. -Set the `immediate` option to `true` to invoke the function immediately at the start of the `wait` interval, preventing issues such as double-clicks on a button. +Set the `immediate` option to `true` to execute the function immediately at the start of the `wait` interval, preventing issues such as double-clicks on a button. -The returned function has a `.clear()` method to cancel scheduled executions, and a `.flush()` method for immediate execution and resetting the timer for future calls. +The returned function has the following methods: + +- `.clear()` cancels any scheduled executions. +- `.flush()` if an execution is scheduled then it will be immediately executed and the timer will be cleared. +- `.trigger()` executes the function immediately and clears the timer if it was previously set. */ declare function debounce( function_: F, @@ -18,6 +22,7 @@ declare namespace debounce { (...arguments_: Parameters): ReturnType | undefined; clear(): void; flush(): void; + trigger(): void; }; } diff --git a/index.js b/index.js index 6899c2f..6d6a826 100644 --- a/index.js +++ b/index.js @@ -16,6 +16,15 @@ function debounce(function_, wait = 100, options = {}) { let timestamp; let result; + function run() { + const callContext = storedContext; + const callArguments = storedArguments; + storedContext = undefined; + storedArguments = undefined; + result = function_.apply(callContext, callArguments); + return result; + } + function later() { const last = Date.now() - timestamp; @@ -25,11 +34,7 @@ function debounce(function_, wait = 100, options = {}) { timeoutId = undefined; if (!immediate) { - const callContext = storedContext; - const callArguments = storedArguments; - storedContext = undefined; - storedArguments = undefined; - result = function_.apply(callContext, callArguments); + result = run(); } } } @@ -50,11 +55,7 @@ function debounce(function_, wait = 100, options = {}) { } if (callNow) { - const callContext = storedContext; - const callArguments = storedArguments; - storedContext = undefined; - storedArguments = undefined; - result = function_.apply(callContext, callArguments); + result = run(); } return result; @@ -74,14 +75,13 @@ function debounce(function_, wait = 100, options = {}) { return; } - const callContext = storedContext; - const callArguments = storedArguments; - storedContext = undefined; - storedArguments = undefined; - result = function_.apply(callContext, callArguments); + debounced.trigger(); + }; - clearTimeout(timeoutId); - timeoutId = undefined; + debounced.trigger = () => { + result = run(); + + debounced.clear(); }; return debounced; diff --git a/readme.md b/readme.md index 07c7e16..0e9e8e2 100644 --- a/readme.md +++ b/readme.md @@ -29,21 +29,31 @@ To later clear the timer and cancel currently scheduled executions: window.onresize.clear(); ``` -To execute any pending invocations and reset the timer: +To execute immediately only if you have scheduled invocations and reset the timer: ```js window.onresize.flush(); ``` +To execute immediately and reset the timer if it was previously set: + +```js +window.onresize.trigger(); +``` + ## API ### debounce(fn, wait, options?) Creates a debounced function that delays execution until `wait` milliseconds have passed since its last invocation. -Set the `immediate` option to `true` to invoke the function immediately at the start of the `wait` interval, preventing issues such as double-clicks on a button. +Set the `immediate` option to `true` to execute the function immediately at the start of the `wait` interval, preventing issues such as double-clicks on a button. + +The returned function has the following methods: -The returned function has a `.clear()` method to cancel scheduled executions, and a `.flush()` method for immediate execution and resetting the timer for future calls. +- `.clear()` cancels any scheduled executions. +- `.flush()` if an execution is scheduled then it will be immediately executed and the timer will be cleared. +- `.trigger()` executes the function immediately and clears the timer if it was previously set. ## Related diff --git a/test.js b/test.js index 35f5be3..054602e 100644 --- a/test.js +++ b/test.js @@ -397,3 +397,38 @@ test('calling flush method without any scheduled execution', async () => { assert.strictEqual(callback.callCount, 0, 'Callback should not be executed if flush is called without any scheduled execution'); }); + +test('calling the trigger function should run it immediately', async () => { + const clock = sinon.useFakeTimers(); + const callback = sinon.spy(); + const fn = debounce(callback, 100); + + fn(); + fn.trigger(); + + assert.strictEqual(callback.callCount, 1, 'Callback should be called once when using trigger method'); + + clock.tick(100); + + assert.strictEqual(callback.callCount, 1, 'Callback should stay at one call after timeout'); + + clock.restore(); +}); + +test('calling the trigger should not affect future function calls', async () => { + const clock = sinon.useFakeTimers(); + const callback = sinon.spy(); + const fn = debounce(callback, 100); + + fn(); + fn.trigger(); + fn(); + + assert.strictEqual(callback.callCount, 1, 'Callback should be called once when using trigger method'); + + clock.tick(100); + + assert.strictEqual(callback.callCount, 2, 'Callback should total two calls after timeout'); + + clock.restore(); +});