Both debounce and throttle are techniques used to improve website performance by controlling how often a function is executed. However, they do so in fundamentally different ways.
The Core Difference
- Debounce delays the execution of a function until a certain amount of time has passed since the last time the event was triggered. It groups multiple sequential calls into a single execution.
- Analogy: An elevator. The doors won't close until there has been 10 seconds of silence (no one pressing the button). If someone presses the button, the 10-second timer resets.
- Throttle guarantees that a function is executed at most once in a specified time interval, regardless of how many times the event is triggered.
- Analogy: A faucet dripping water. No matter how much pressure is behind it, it will only release a drop once every 2 seconds.
Which one should you use?
1. Auto-saving form inputs: Use Debounce
You want to wait until the user has finished typing (or paused for a moment) before sending an API request. Using throttle here would trigger multiple intermediate saves while the user is mid-sentence, causing unnecessary database writes.
2. Handling a resize event: Use Throttle (or Debounce)
If you need to recalculate layout positions smoothly as the user drags the window corner, throttle ensures the UI updates at a steady, controlled pace (e.g., every 100ms) without lagging the browser. (Note: If you only need to run calculations after the user is completely done resizing, debounce is also a valid choice).
Code Examples
Here is how you can implement both patterns in a React application using lodash utilities.
1. Debounce Example: Form/Search Input (Auto-save pattern)
Using useMemo ensures that the debounced function is not re-created on every render, preserving its internal timer.
2. Throttle Example: Scroll/Resize Tracker
Throttling ensures that the window scroll position or resize dimension is tracked at a steady pace (every 300ms) during continuous movement, instead of firing hundreds of times per second.
1import { useState, useMemo } from "react";
2import debounce from "lodash/debounce";
3
4function AutoSaveInput() {
5 const [query, setQuery] = useState("");
6
7 // Debounce the save action for 500ms after typing stops
8 const autoSave = useMemo(
9 () =>
10 debounce((value) => {
11 console.log("Saving data to API:", value);
12 }, 500),
13 []
14 );
15
16 const handleChange = (e) => {
17 setQuery(e.target.value);
18 autoSave(e.target.value); // Trigger debounced function
19 };
20
21 return (
22 <input
23 value={query}
24 onChange={handleChange}
25 placeholder="Type to auto-save..."
26 />
27 );
28}
1import { useState, useMemo } from "react";
2import debounce from "lodash/debounce";
3
4function AutoSaveInput() {
5 const [query, setQuery] = useState("");
6
7 // Debounce the save action for 500ms after typing stops
8 const autoSave = useMemo(
9 () =>
10 debounce((value) => {
11 console.log("Saving data to API:", value);
12 }, 500),
13 []
14 );
15
16 const handleChange = (e) => {
17 setQuery(e.target.value);
18 autoSave(e.target.value); // Trigger debounced function
19 };
20
21 return (
22 <input
23 value={query}
24 onChange={handleChange}
25 placeholder="Type to auto-save..."
26 />
27 );
28}
1import { useEffect } from "react";
2import throttle from "lodash/throttle";
3
4function ScrollTracker() {
5 useEffect(() => {
6 // Limits execution to once every 300ms during continuous scrolling
7 const handleScroll = throttle(() => {
8 console.log("Current Scroll Y:", window.scrollY);
9 }, 300);
10
11 window.addEventListener("scroll", handleScroll);
12
13 // Clean up event listener on unmount
14 return () => {
15 window.removeEventListener("scroll", handleScroll);
16 };
17 }, []);
18
19 return (
20 <div style={{ height: "200vh" }}>
21 Scroll down to see throttling in action!
22 </div>
23 );
24}
1import { useEffect } from "react";
2import throttle from "lodash/throttle";
3
4function ScrollTracker() {
5 useEffect(() => {
6 // Limits execution to once every 300ms during continuous scrolling
7 const handleScroll = throttle(() => {
8 console.log("Current Scroll Y:", window.scrollY);
9 }, 300);
10
11 window.addEventListener("scroll", handleScroll);
12
13 // Clean up event listener on unmount
14 return () => {
15 window.removeEventListener("scroll", handleScroll);
16 };
17 }, []);
18
19 return (
20 <div style={{ height: "200vh" }}>
21 Scroll down to see throttling in action!
22 </div>
23 );
24}