diff --git a/index.d.ts b/index.d.ts index d490f67..1b10427 100644 --- a/index.d.ts +++ b/index.d.ts @@ -10,7 +10,7 @@ The returned function has a `.clear()` method to cancel scheduled executions, an declare function debounce( function_: F, wait?: number, - immediate?: boolean + options?: {immediate: boolean} ): debounce.DebouncedFunction; declare namespace debounce { diff --git a/index.js b/index.js index bdd851f..ea5dd76 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,11 @@ -function debounce(function_, wait = 100, immediate) { +function debounce(function_, wait = 100, options = {}) { + if (wait < 0) { + throw new RangeError('`wait` must not be negative.'); + } + + // TODO: Deprecate the boolean parameter at some point. + const {immediate} = typeof options === 'boolean' ? {immediate: options} : options; + let storedContext; let storedArguments; let timeoutId; diff --git a/readme.md b/readme.md index 7586b89..d70ad8a 100644 --- a/readme.md +++ b/readme.md @@ -21,6 +21,8 @@ function resize() { window.onresize = debounce(resize, 200); ``` +*(You can also use `const debounce = require('debounce')`)* + To later clear the timer and cancel currently scheduled executions: ```js @@ -35,11 +37,11 @@ window.onresize.flush(); ## API -### debounce(fn, wait, immediate?) +### debounce(fn, wait, options?) Creates a debounced function that delays execution until `wait` milliseconds have passed since its last invocation. -Set the `immediate` parameter 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 invoke 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. diff --git a/test.js b/test.js index 8b2c614..662f244 100644 --- a/test.js +++ b/test.js @@ -168,3 +168,53 @@ test('context check in debounced function', async t => { assert.ok(errorThrown, 'An error should have been thrown'); }); }); + +test('immediate execution', async t => { + let clock; + + await t.test('should execute immediately when immediate is true', async () => { + clock = sinon.useFakeTimers(); + const callback = sinon.spy(); + + const fn = debounce(callback, 100, true); + + fn(); + assert.strictEqual(callback.callCount, 1, 'Callback should be triggered immediately'); + + clock.tick(100); + assert.strictEqual(callback.callCount, 1, 'Callback should not be triggered again after wait time'); + + clock.restore(); + }); + + await t.test('should execute immediately when immediate is in options object', async () => { + clock = sinon.useFakeTimers(); + const callback = sinon.spy(); + + const fn = debounce(callback, 100, {immediate: true}); + + fn(); + assert.strictEqual(callback.callCount, 1, 'Callback should be triggered immediately'); + + clock.tick(100); + assert.strictEqual(callback.callCount, 1, 'Callback should not be triggered again after wait time'); + + clock.restore(); + }); + + await t.test('should not execute immediately when immediate is false', async () => { + clock = sinon.useFakeTimers(); + const callback = sinon.spy(); + + const fn = debounce(callback, 100, {immediate: false}); + + fn(); + clock.tick(50); + assert.strictEqual(callback.callCount, 0, 'Callback should not be triggered immediately'); + + clock.tick(50); + assert.strictEqual(callback.callCount, 1, 'Callback should be triggered after wait time'); + + clock.restore(); + }); +});