From 87d19cf18f92cbebbd20e38d51786144fa64ee94 Mon Sep 17 00:00:00 2001 From: Vladyslav Dukhin <15850680+primeare@users.noreply.github.com> Date: Tue, 29 Nov 2022 00:16:15 +0200 Subject: [PATCH 1/3] Call original debounced function with proper "this" - reuse timer if the refresh method is available - call original function with "this" of debounced function --- lib/throttle.js | 17 +++++++++-------- test/throttle.js | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/lib/throttle.js b/lib/throttle.js index b634039a..070a9a7a 100644 --- a/lib/throttle.js +++ b/lib/throttle.js @@ -39,15 +39,16 @@ const throttle = (timeout, fn, ...args) => { // args - , arguments for fn, optional const debounce = (timeout, fn, ...args) => { let timer; - - const debounced = () => (args ? fn(...args) : fn()); - - const wrapped = () => { - if (timer) clearTimeout(timer); - timer = setTimeout(debounced, timeout); + return function () { + if (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/throttle.js b/test/throttle.js index 150fe2f1..32568f26 100644 --- a/test/throttle.js +++ b/test/throttle.js @@ -101,6 +101,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) => { From eb5cb98f47d3818234e3854789c8d492ae4587f3 Mon Sep 17 00:00:00 2001 From: Vladyslav Dukhin <15850680+primeare@users.noreply.github.com> Date: Mon, 5 Dec 2022 03:46:08 +0200 Subject: [PATCH 2/3] feat: ability to call debounced with new arguments - fix backward compatibility with Node.js 12 - add ability to call debounced function with new arguments --- lib/throttle.js | 15 ++++++++++----- test/throttle.js | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/throttle.js b/lib/throttle.js index 070a9a7a..c134612d 100644 --- a/lib/throttle.js +++ b/lib/throttle.js @@ -33,14 +33,19 @@ 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; - return function () { - if (timer?.refresh !== undefined) { + 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(); diff --git a/test/throttle.js b/test/throttle.js index 32568f26..07973c64 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; From 8754bd076c6ae1291cf6573c1ea52273eae7421e Mon Sep 17 00:00:00 2001 From: Vladyslav Dukhin <15850680+primeare@users.noreply.github.com> Date: Mon, 5 Dec 2022 04:01:45 +0200 Subject: [PATCH 3/3] fix: debounce example test to use new arguments --- test/examples.js | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/test/examples.js b/test/examples.js index 1aa5c9f2..da55c09a 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);