Debouncing vs. Throttling: Unraveling the Differences and Benefits

Debouncing vs. Throttling: Unraveling the Differences and Benefits

Throttling and Debouncing: The Superheroes of Event Handling

In the world of JavaScript event handling, debouncing and throttling are like superheroes that swoop in to save the day. These techniques allow us to control how many times a function gets called when an event fires repeatedly. Without them, our apps would be sluggish, make too many API calls, and overload users with updates.

But like any superhero duo, throttling and debouncing have their own distinct styles and use cases. Understanding how they complement each other is key to utilizing them effectively in your code.

Debouncing vs. Throttling

Why you should care?

Here are some reasons why mastering throttling and debouncing should be on every JavaScript developer’s bucket list:

  • Improve app performance by eliminating unnecessary computations and API calls

  • Create smoother UIs by controlling update frequency

  • Reduce server load by limiting requests

  • Optimize for user experience over machine optimization

  • Avoid rate limits and extra costs from over-calling APIs

  • Help tame hard-to-reproduce concurrency bugs

Let’s break down how these two techniques work, when to use each one, and how you can wield them in your apps.

Understanding the Basics

What is Debouncing?

Debouncing enforces that a function will not be called again until a certain amount of time has passed without it being called. This ensures that the function is only invoked after the input/event is completed or has stopped for a given duration.

“Wait a sec! Debouncing in Action”

For example, debouncing a button click handler by 250ms would mean that no matter how many times the button is clicked within that timeframe, the handler will only be invoked once 250ms after the last click.

What is Throttling?

Throttling limits the rate at which a function can be called over time. This is done by allowing a function to execute at most once during a given window of time.

“The Speed Limit! Throttling in Action”

For example, throttling a button click handler by 250ms would mean the handler can only be invoked once every 250ms, no matter how many actual clicks occur during that time.

Diving Deeper into Debouncing

How Debouncing Works

Under the hood, a debounced function contains a timer that tracks elapsed time between function calls. Each time the debounced function is invoked, the timer is cleared and reset.

This means that as long as the wrapped function keeps getting called within the specified time interval, the underlying function will not be executed.

Only when enough time has elapsed without any further calls, will the underlying function finally be invoked.

The Art of Holding Back: Delaying Function Invocation

Here is some pseudocode to illustrate the debounce logic:

let timeout;

function debounce(func, delay) {

  clearTimeout(timeout);

  timeout = setTimeout(() => {
    func();
  }, delay);
}

By resetting the timer each time, we ensure func() won’t run until after input has stopped for delay milliseconds.

Real World Examples of Debouncing

Here are some common use cases where debouncing shines:

  • Search boxes — debounce API calls triggered on each keystroke

  • Window resize handlers — debounce to avoid resize thrashing

  • Scroll handlers — debounce to prevent scroll overload

  • Button clicks — debounce to prevent accidental double submits

  • Mouse movement — debounce for smoother painting/drawing

Debouncing code example in React.js

Here is an example of debouncing a search API call in React:

import { useState, useCallback } from 'react';

function Search() {
  const [searchTerm, setSearchTerm] = useState('');

  const debouncedSearch = useCallback(
    debounce((term) => {
      // Call API with the latest search term after 500ms of inactivity
      doSearch(term);
    }, 500),
    []
  );

  function doSearch(term) {
    // Perform API call with the latest search term
    // This function will be called after a 500ms inactivity period
  }

  function handleChange(e) {
    const { value } = e.target;
    setSearchTerm(value);
    debouncedSearch(value); // Debounced search API call
  }

  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={handleChange}
        placeholder="Search..."
      />
    </div>
  );
}

Less is More: Reducing API Calls with Debouncing

  • Avoids needlessly calling APIs on every input event

  • Saves server resources by limiting requests

  • Prevents hitting rate limits on APIs

  • Creates smoother UIs by eliminating premature updates

  • Ensures function is only called when input is truly finished

Diving Deeper into Throttling

How Throttling Works

Under the hood, throttling also uses a timer to track elapsed time. But unlike debouncing, it ensures the wrapped function is called at regular intervals as long as input continues.

Each time the throttled function is called, it checks if enough time has passed since the last invocation. If yes, it makes the call, otherwise it skips execution.

Slow and Steady: Limiting Function Invocation

Here is some pseudocode showing the throttle logic:

let timeout;

function throttle(func, limit) {

  if (!timeout) {
    func();
    timeout = setTimeout(() => {
      timeout = null;
    }, limit);
  }
}

This guarantees func() is called at most once every limit milliseconds.

Real World Examples of Throttling

Here are some common throttling use cases:

  • Scroll handlers — throttle to improve performance

  • Resize handlers — throttle to avoid resize thrashing

  • Save buttons — throttle to prevent duplicate submissions

  • Live search boxes — throttle to limit network requests

  • Game loops — throttle to cap frame rates

Throttling code example in React.js

Here is an example of throttling a scroll handler in React:

import { useCallback, useRef, useEffect } from 'react';

function ScrollTracker() {
  const prevScrollY = useRef(0);

  // Throttle the handleScroll function to execute at most once every 100ms
  const throttledScroll = useCallback(
    throttle(handleScroll, 100),
    [],
  );

  function handleScroll() {
    const currentScrollY = window.scrollY;

    if (currentScrollY < prevScrollY.current) {
      console.log('scrolling up');
    } else {
      console.log('scrolling down');
    }

    prevScrollY.current = currentScrollY;
  }

  useEffect(() => {
    // Add the throttledScroll function as the event listener for the scroll event
    window.addEventListener('scroll', throttledScroll);

    return () => {
      // Clean up by removing the event listener when the component is unmounted
      window.removeEventListener('scroll', throttledScroll);
    };
  }, [throttledScroll]);

  return null; 

export default ScrollTracker;

Here we throttle the scroll handler to cap how often it runs during continuous scrolling.

Keeping it Smooth: Ensuring Consistent Performance with Throttling

  • Prevents UI overload by limiting update frequency

  • Avoids janky/stuttering animations and scrolling

  • Improves consistency of framerates in games

  • Ratelimits network requests during continuous events

  • Ensures function is called at regular controlled intervals

The Great Divide — Debouncing vs. Throttling

Now that you have an intuition for how debouncing and throttling work, let’s highlight the key differences:

CriteriaDebouncingThrottling
Function InvocationDelays invoking function until input pausesInvokes function at most once over a time period
Use CasesReducing high-frequency expensive operationsRate-limiting UI updates and preventing duplicates
Execution RequirementWaits for a pause in input to executeCalls the function at regular intervals
Input HandlingCaptures the last input during the pauseCaptures and processes every input during the period
Time GranularitySuitable for longer pause periodsSuitable for shorter time intervals
Event TriggeringUsually triggered on input releaseUsually triggered on input press or release

When to use which? Making the Right Choice

Here are some rules of thumb on when to debounce vs throttle:

  • Use debouncing for expensive operations like API calls to avoid overloading.

  • Use throttling to limit UI update frequency and maintain visual consistency.

  • Debounce one-off events like button clicks.

  • Throttle repetitive events like window resizing and scrolling.

  • Debounce when you only care about the final state after input has stopped.

  • Throttle when you want intermediate updates at a controlled rate.

Conclusion

Debouncing and throttling are invaluable techniques for optimizing application performance. While their implementations differ, debouncing and throttling serve complementary purposes. Debouncing reduces noise from intermittent events, while throttling smooths out rapid-fire triggers.

Understanding when to reach for which technique takes some experience. But by mastering both, you can build apps that are efficient, responsive, and delightful.

So next time your users are furiously tapping and scrolling, don’t panic. Just take a deep breath, and reach for debounce and throttle — your new superhero BFFs.

I hope you found this comprehensive guide on Debouncing and Throttling helpful. If you enjoyed reading it, consider giving it a clap and following me for more content like this. You can also connect with me on LinkedIn and check out my other projects on GitHub.

LinkedIn
GitHub

Stay tuned for more in-depth guides and tutorials!