diff --git a/lib/throttle.js b/lib/throttle.js index b634039..c134612 100644 --- a/lib/throttle.js +++ b/lib/throttle.js @@ -33,21 +33,27 @@ const throttle = (timeout, fn, ...args) => { }; // Debounce function, delayed execution -// Signature: timeout, fn, ...args +// Signature: timeout, fn, ...presetArgs // timeout - , msec // fn - , to be debounced -// args - , arguments for fn, optional -const debounce = (timeout, fn, ...args) => { +// presetArgs - , pre-populated arguments for fn, optional +const debounce = (timeout, fn, ...presetArgs) => { let timer; - - const debounced = () => (args ? fn(...args) : fn()); - - const wrapped = () => { - if (timer) clearTimeout(timer); - timer = setTimeout(debounced, timeout); + return function (...newArgs) { + const args = newArgs.length !== 0 ? newArgs : presetArgs; + if ( + newArgs.length === 0 && + timer !== undefined && + timer.refresh !== undefined + ) { + // if possible, refresh the timer without + // allocating a new JavaScript object + timer.refresh(); + } else { + clearTimeout(timer); + timer = setTimeout(() => fn.apply(this, args), timeout); + } }; - - return wrapped; }; const FN_TIMEOUT = 'Metasync: asynchronous function timed out'; diff --git a/test/examples.js b/test/examples.js index 1aa5c9f..da55c09 100644 --- a/test/examples.js +++ b/test/examples.js @@ -399,43 +399,25 @@ metatests.test('trottle', (test) => { metatests.test('debounce', (test) => { const expectedResult = ['E', 'I']; const result = []; - let state; - const fn = () => { - result.push(state); + const fn = (arg) => { + result.push(arg); }; - const f1 = metasync.debounce(500, fn, ['I']); + const f1 = metasync.debounce(500, fn); // to be called one time (E) - state = 'A'; - f1(); - state = 'B'; - f1(); - state = 'C'; - f1(); - state = 'D'; - f1(); - state = 'E'; - f1(); + f1('A'); + f1('B'); + f1('C'); + f1('D'); + f1('E'); // to be called one time (I) - setTimeout(() => { - state = 'F'; - f1(); - }, 600); - setTimeout(() => { - state = 'G'; - f1(); - }, 700); - setTimeout(() => { - state = 'H'; - f1(); - }, 1000); - setTimeout(() => { - state = 'I'; - f1(); - }, 1100); + setTimeout(() => f1('F'), 600); + setTimeout(() => f1('G'), 700); + setTimeout(() => f1('H'), 1000); + setTimeout(() => f1('I'), 1100); setTimeout(() => { test.strictSame(result, expectedResult); diff --git a/test/throttle.js b/test/throttle.js index 150fe2f..07973c6 100644 --- a/test/throttle.js +++ b/test/throttle.js @@ -67,7 +67,7 @@ metatests.test('throttle without arguments for function', (test) => { test.strictSame(callCount, 1); }); -metatests.test('debounce', (test) => { +metatests.test('debounce with pre-populated arguments', (test) => { let count = 0; const fn = (arg1, arg2, ...otherArgs) => { @@ -85,6 +85,22 @@ metatests.test('debounce', (test) => { test.strictSame(count, 0); }); +metatests.test('debounce with arguments', (test) => { + let count = 0; + + const fn = (...args) => { + test.strictSame(args, [1, 2, 3]); + count++; + test.end(); + }; + + const debouncedFn = metasync.debounce(1, fn); + + debouncedFn(0, 1, 2); + debouncedFn(1, 2, 3); + test.strictSame(count, 0); +}); + metatests.test('debounce without arguments for function', (test) => { let count = 0; @@ -101,6 +117,30 @@ metatests.test('debounce without arguments for function', (test) => { test.strictSame(count, 0); }); +metatests.test('debounce with proper "this" binding', (test) => { + let count = 0; + let originalThis; + + class DebounceTest { + constructor() { + this.debouncedMethod = metasync.debounce(1, this.originalMethod); + originalThis = this; + } + + originalMethod(...args) { + test.strictSame(args, []); + test.strictSame(this, originalThis); + count++; + test.end(); + } + } + + const debounceTestInstance = new DebounceTest(); + debounceTestInstance.debouncedMethod(); + debounceTestInstance.debouncedMethod(); + test.strictSame(count, 0); +}); + metatests.test('timeout with sync function', (test) => { const syncFn = (callback) => callback(null, 'someVal'); metasync.timeout(1, syncFn, (err, res, ...args) => {