BlogJavaScript

When to use throttle vs debounce

Written by Codemzy on October 6th, 2023

Both throttling and debouncing are useful ways to slow down how often a function runs when it's called multiple times. Throttling allows a function to be called once during a timeframe, and debouncing calls a function once after the timeframe.

throttle vs debounce image

Both throttling and debouncing are ways to reduce the amount of times a function is run, no matter how many times it is called.

When a function is called multiple times, and too quickly for what you want to do, throttling and debouncing can slow it down.

But that's where the similarities end.

Throttling

  • Runs a function when it is called
  • Doesn't run the function if it has already run within the timeframe given

When you throttle a function, you say it can only run once every X amount of time.

If you say a function is throttled for 1000ms, then no matter how many times it is called, it will only run once every second.

throttle diagram

The throttled function runs on the first call, and afterwards, it will only run the function once every X amount of time. There's no delay, the throttle will just ignore any function calls if the function has already been run within the specified timeframe.

Let's imagine someone asks you for a hug, and you give them one. But then they ask you for another hug, but you don't want to hug them again because they just had one! And they keep asking every 5 minutes for another hug. So you say they can only have one hug every half an hour no matter how many times they ask - that's throttling.

Debouncing

  • Runs a function after it is called (delay)
  • Resets the delay if it is called again within the timeframe given

When you debounce a function, you say it can only run after X amount of time has passed since the last time the function was called.

If you say a function is debounced for 1000ms, but it's called every 500ms for an hour, it will only eventually run after 1 hour and 1 second (one second since it was last called).

debounce diagram

The debounce function does not run on the first call. When the first function call is made, it delays running the function by X amount of time.

If the function is called again within the timeframe, the previous function call is cancelled and the delay starts again. This repeats and the function will only run if the specified time is reached without the function being called again.

debounce infographic

This time when someone asks you for a hug, you say they can have one in 20 minutes. But they ask you again for a hug 5 minutes later, and you say they can have one in 20 minutes. And they ask you again 2 minutes later. And you say they can only have a hug after they have stopped asking for you for one for at least 20 minutes - that's debouncing.

When to use throttle

  • for running a function as things happen
  • to slow down how often the function runs

You should use throttle when you want to run a function multiple times, but it is being called too frequently. Throttling can help you slow it down.

This can sometimes be the case for events that are outside of your control.

For example, if you want to run an animation as a user moves their mouse across the screen, the mousemove event can trigger hundreds of times a second.

Running a function that often to trigger an animation is going to freeze up the browser and make the mouse movement appear pretty janky - not good for your user who's expecting a magical animation!

Here's an example of that disaster:

var throttled = _.throttle((e) => {
  confetti({
    particleCount: 100,
    spread: 70,
    origin: { y: e.pageY/window.innerHeight, x: e.pageX/window.innerWidth },
  });
}, 500);

document.addEventListener("mousemove", (e) => {
  confetti({
    particleCount: 100,
    spread: 70,
    origin: { y: e.pageY/window.innerHeight, x: e.pageX/window.innerWidth },
  });
});

But you do want to run the animation as they move the mouse (and not after it finishes). Throttling is perfect for this use case.

You can't reduce the frequency of the "mousemove" event calling your function - that's in the user's hands. But you can throttle that function down to run a maximum of once every 500ms, no matter how much the user moves the mouse.

Here's how that looks:

let throttled = _.throttle((e) => {
  confetti({
    particleCount: 100,
    spread: 70,
    origin: { y: e.pageY/window.innerHeight, x: e.pageX/window.innerWidth },
  });
}, 500);

document.addEventListener("mousemove", (e) => {
  throttled(e);
});

The browser is smooth, the animation is superb, and everyone is happy again*.

*No! I would never do this on a real website - but you get the idea! 😆

Throttling is great for any function you want to run in response to a browser event, like scrolling, mouse movements, window resizing etc.

When to use debounce

  • for running a function after things happen
  • to delay a function until after an event stops

Debounce is a good choice when you want an event to trigger a function, but you don't want that function to run until after the event has finished.

For example, if a user is typing in a search input and you want to automatically run the search when they've typed something. No button to click.

Let's say they are searching for "When to use debounce". And you are listening to the "change" event on the input. You don't really want to waste server calls returning results for "w", "wh", "whe", "when", or even "when to use". Because it's not going to give the user the information they want.

Instead, you can use debounce to save when they finish (or pause) typing for a couple of seconds.

I've also used debounce when I set up an autosave for a text editor. I don't want to save every time a user types a letter, but I do want to save all the recent changes when they pause typing for 5 seconds.

Here's the debounce function I use - but you can also find debounce functions in JavaScript libraries like lodash if you prefer.

// debounce function (defaults wait to .2 seconds)
const debounce = (func, wait = 200) => {
    let timeout; // for the setTimeout function and so it can be cleared
    return function executedFunction(...args) { // the function returned from debounce
        const later = () => { // this is the delayed function
            clearTimeout(timeout); // clears the timeout when the function is called
            func(...args); // calls the function
        };
        clearTimeout(timeout); // this clears the timeout each time the function is run again preventing later from running until we stop calling the function
        timeout = setTimeout(later, wait); // this sets the time out to run after the wait period
    };
};

Let's go back to our confetti mouse demo. What if we just want to throw some confetti when the mouse finishes moving (not while it's moving)? Instead of throttle, we can debounce so that the confetti happens after the event instead.

let debounced = _.debounce((e) => {
  confetti({
    particleCount: 100,
    spread: 70,
    origin: { y: e.pageY/window.innerHeight, x: e.pageX/window.innerWidth },
  });
}, 500);

document.addEventListener("mousemove", (e) => {
  debounced(e);
});

In the debounced demo above, instead of running every 500ms while the mouse moves (like the throttled demo), it runs 500ms after the mouse stops moving.


🎉 You probably never want a confetti animation on your mouse in the real world, but hopefully the demos give you an idea of how throttle and debounce work!