Skip to content

Debounce

See Glossary: debounce vs throttling

Always use arrow functions so that we can use the this context

Example usage

usage.js
let counter = 1;

const print = () => {
    console.log("Ran print");
};
const debouncedPrint = debounce(print, 1000);

const timer = setInterval(() => {
    debouncedPrint();

    counter += 1;
    console.log(`Debug: counter: ${counter}`);

    if (counter === 10) {
        clearInterval(timer);
    }
}, 100);

Basic debounce (no args and return value)

  • arguments and return value
debouncev1.js
function debounce(fn, delay) {
    let timeoutId;

    function debouncedFn() {
        // everytime the function gets called, restart the timer
        clearTimeout(timeoutId);
        const startTimer = () => {
            timeoutId = setTimeout(() => {
                fn();
            }, delay);
        };
        startTimer();
    }

    return debouncedFn;
}

Debounce with args

function debounce(fn, delay) {
    let timeoutId;

-   function debouncedFn() {
+   function debouncedFn(...args) {
        clearTimeout(timeoutId);

       const startTimer = () => {
            timeoutId = setTimeout(() => {
-               fn();
+               fn.apply(null, args);
            }, delay);
        };
        startTimer();
    }

    return debouncedFn;
}

Debounce that returns a promise

?????????

function debounce(fn, delay) {
    let timeoutId;
+   const pending = [];

    function debouncedFn(...args) {
+       const prom = new Promise((res, rej) => {
            clearTimeout(timeoutId);
            const startTimer = () => {
                timeoutId = setTimeout(() => {
+                   const currentPending = [...pending];
+                   pending.length = 0;
-                   fn.apply(null, args);
+                   Promise.resolve(fn.apply(this, args)).then(
+                       data => {
+                           currentPending.forEach(({ resolve }) => resolve(data))
+                       }
+                       error => {
+                           currentPending.forEach(({ reject }) => reject(error));
+                       }
+                   );
                }, delay);
            };
            startTimer();
+           pending.push({ resolve: res, reject: rej });
+       })
    }

    return debouncedFn;
}

Debounce with promise example

const fn = arg => new Promise(resolve => {
  setTimeout(resolve, 1000, ['resolved', arg]);
});
const debounced = debouncePromise(fn, 200);
debounced('foo').then(console.log);
debounced('bar').then(console.log);
// Will log ['resolved', 'bar'] both times

Last update: 2022-09-23