Debounce vs. Throttle: Decoding the Duel

Debounce vs. Throttle: Decoding the Duel

Elevate Your React or JavaScript-based Application Performance to the Next Level!

As developers, we always strive to improve the performance of our applications. Two techniques that can significantly enhance web application performance are debouncing and throttling. In this article, we will explore these techniques, understand their differences, and see how they can be effectively used. So without wasting any time, let’s dive in and explore these techniques with examples.

Debouncing

Debouncing is a technique that limits the frequency of function calls within a specified timeframe. By preventing rapid execution of a specific function, debouncing enhances performance in situations where the function is triggered frequently, such as user input events like keystrokes, window resizing, or scroll events. Let’s delve into the specific problem and explore how debouncing can be applied to solve it effectively.

Actual Problem

Consider a situation we have an e-commerce website where we have a search box with an input field that triggers a search function every time the user types a character. Without debouncing, the search function would be called with each keystroke, potentially overloading the server and impacting the application’s responsiveness and overall performance.

Solution

We can wrap the search function with debouncing function to address this issue. Let’s see this with an example.

Javascript

const input = document.querySelector(".custom-input");

// ❌ Before implementation
const searchResults = (event) => {
  // Your core logic will be here!
  console.log(event.target.value);
};

if (input) {
  // Triggers for each keystrokes
  input.addEventListener("input", searchResults);
}

// ✅ After implementation
const debounce = (cb, delay) => {
  let debounceTime;

  return (...args) => {
    clearTimeout(debounceTime);
    debounceTime = setTimeout(() => {
      cb(...args);
    }, delay);
  };
};

if (input) {
  // being debounced with a delay of 500 milliseconds
  input.addEventListener("input", debounce(searchResults, 500));
}

Reference: https://codesandbox.io/s/debounce-with-js-jjc9yx

React

// Here, we implemented debouncing with React new hook called `useDeferredValue.`
export default function App() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);

  return (
    <>
      <label>
        Search albums:
        <input value={query} onChange={e => setQuery(e.target.value)} />
      </label>
    </>
  );
}

Reference: https://react.dev/reference/react/useDeferredValue#usedeferredvalue

Throttling

Throttling is a technique that controls function execution rate for a specific timeframe. It restricts the frequency of function executions by adding a minimum time interval between consecutive calls. It helps regulate the execution rate and prevent excessive function calls, ensuring optimal performance and resource utilization. Now, let’s explore a specific problem and demonstrate how throttling can be used to address it.

Actual Problem

When a user resizes the window, the resize event fires continuously, triggering expensive operations such as recalculating layouts or re-rendering elements. These operations can overwhelm the browser without throttling and lead to a sluggish user experience.

Solution

Throttling the window resize event ensures that the expensive operations are executed at a controlled rate, preventing excessive function invocations and optimizing performance.

Here’s an example:

Javascript

const height = document.querySelector("#height");
const width = document.querySelector("#width");

// ❌ Before implementation
// invokes for each change
const handleResize = () => {
  heightOutput.textContent = window.innerHeight;
  widthOutput.textContent = window.innerWidth;
  // Your business logic over here
};

(() => {
  handleResize();
})();

window.addEventListener("resize", handleResize);

// ✅ After implementation
// invoke only once every 500 milliseconds (given delay)

const throttle = (cb, delay) => {
  let throttling = false;

  return (...args) => {
    if (!throttling) {
      throttling = true;

      cb(...args);

      setTimeout(() => {
        throttling = false;
      }, delay);
    }
  };
};

window.addEventListener("resize", throttle(handleResize, 500));

Reference: https://codesandbox.io/s/throttle-in-js-4zkk5j

React

import { useEffect, useRef } from "react";

export default function App() {
  const heightRef = useRef();
  const widthRef = useRef();

  const throttle = (cb, delay) => {
    let throttling = false;

    return (...args) => {
      if (!throttling) {
        throttling = true;

        cb(...args);

        setTimeout(() => {
          throttling = false;
        }, delay);
      }
    };
  };

  useEffect(() => {
    const handleResize = () => {
      heightRef.current.textContent = window.innerHeight;
      widthRef.current.textContent = window.innerWidth;
      // Your business logic over here
    };
    window.addEventListener("resize", throttle(handleResize, 500));

    return () => {
      window.removeEventListener("resize", throttle(handleResize, 500));
    };
  }, []);

  return (
    <div>
      <p>
        Current window width: <span ref={widthRef}></span>
      </p>
      <p>
        Current window height: <span ref={heightRef}></span>
      </p>
    </div>
  );
}

Reference: https://codesandbox.io/s/throttling-with-reactjs-gxqyx4

Conclusion

Both throttle and debounce are valuable techniques in JavaScript for managing the frequency of function invocations. While they serve similar purposes of optimizing performance and controlling execution rates, they differ in handling consecutive function calls.

Throttling ensures a function is executed at a controlled rate by enforcing a minimum interval between consecutive calls. Debouncing, on the other hand, delays the execution of a function until there is a pause or inactivity in function calls for a specified period.

Both techniques help optimize performance, reduce unnecessary computations, and improve user experience by controlling the frequency of function executions. Understanding their differences and choosing the right technique based on your use case will enable you to design more efficient and responsive JavaScript applications.

I hope this article is helpful to you!

Stay curious; keep coding!