Dealing with Debounce and Throttle
A Comprehensive Guide to mastering the events

When developing web applications and dealing with on-screen interactions, we don't always want an action to occur immediately upon triggering an event. This is particularly true when the action involves an HTTP request to an external service.
Debounce and throttle techniques come into play as effective solutions to delay specific events for a defined period, providing more precise control over the execution flow.
A common use case
Imagine a fintech that seeks to enhance the user experience in its web application, making it more efficient and responsive. When dealing with interaction events in the interface, the development team faces the challenge of managing excessive API calls, especially during operations involving real-time queries and frequent updates.
To address this challenge, the fintech decides to implement debounce and throttle techniques at strategic points in the application.
In the case of balance queries, where users tend to repeatedly check their balance, the team chooses to apply the debounce technique. In this way, the balance query is delayed and is only executed after a short period of user inactivity. This prevents multiple unnecessary calls, optimizing the system's efficiency.
As for real-time updates, crucial for displaying recent transactions, the fintech decides to implement throttle. This way, the frequency of updates is controlled, ensuring that only one request is sent every X seconds. This not only provides relevant information to users but also prevents overloading the server with excessive calls.
The Debounce technique
Debounce is a strategy to control the execution frequency of a function by delaying it until no new calls occur for a specified period. This is especially useful when dealing with input events, such as typing in search fields or resizing windows.
Imagine a coffee filter as a search field. ☕️ 🤓
Each time someone pours water over the ground coffee, it takes time for the water to pass through the filter before reaching the pot. Debounce works similarly, preventing a function from being called immediately with every event, allowing time for things to "settle" before being processed.
A practical example
In a React application, we want to perform a search as the user types in a search field. Without debounce, the search function would be triggered with every keystroke, leading to multiple unnecessary calls.
import { FC, ChangeEvent, useState } from "react";
import { debounce } from "lodash";
const SearchComponent: FC = () => {
const [query, setQuery] = useState("");
// Debounced search function
const performSearch = debounce((searchQuery: string) => {
// Search logic here
console.log(`Performing search for: ${searchQuery}`);
}, 500); // 500 milliseconds debounce
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
setQuery(value);
// On each input, trigger the debounced function
performSearch(value);
};
return (
<input
type="text"
value={query}
onChange={handleInputChange}
placeholder="Type to search"
/>
);
};
export default SearchComponent;
In this example, the performSearch function is debounced using the Lodash library. With each keystroke in the search field, the function is called, but the actual search is only performed after a 500-millisecond interval since the last typing event.
The Throttle technique
Throttle is a strategy that limits the frequency of execution of a function, ensuring that it is only called at specific time intervals, even if more frequent events occur. This is particularly useful in cases such as window resizing or continuous scrolling, where excessive calls can negatively impact performance.
Imagine a water tap as a continuous event, like scrolling through a web page. Without this technique, water flows freely, potentially causing wastage. With the throttle technique, we can control the flow of water to ensure more efficient usage, releasing only the necessary amount at regular intervals.
A practical example
In a React application, we want to perform a component update in response to window resize events. We will use the throttle technique to ensure that the update occurs at spaced intervals, even if the resize event is triggered frequently.
import { FC, useState, useEffect } from "react";
import { throttle } from "lodash";
const ResizeComponent: FC = () => {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
// Resize function to be throttled
const handleResize = throttle(() => {
setWindowSize({ width: window.innerWidth, height: window.innerHeight });
}, 500);
// Throttle of 500 milliseconds
useEffect(() => {
// Adds the resize event listener
window.addEventListener("resize", handleResize);
// Removes the listener when the component is unmounted
return () => {
window.removeEventListener("resize", handleResize);
};
}, [handleResize]);
const { width, height } = windowSize;
return (
<div>
<p>Width: {width}</p>
<p>Height: {height}</p>
</div>
);
};
export default ResizeComponent;
In this example, the handleResize function is throttled using the Lodash library. State updates occur only every 500 milliseconds, even if the resize event is triggered more frequently. This ensures a smooth response to changes in window size without overloading the component with excessive updates.
Bonus: Creating your own functions
Well, let's say you don't want to use the Lodash library and prefer to create your own functions to implement debounce and throttle techniques.
Here is a simple example of how you can create a debounce function from scratch:
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// Example of usage
const executeWithDebounce = debounce(() => {
console.log("Debounced function executed!");
}, 1000);
// Invokes the debounced function after 500 milliseconds
executeWithDebounce();
And here is a basic example of creating a throttle function from scratch:
function throttle(func, delay) {
let isThrottled = false;
return function (...args) {
if (!isThrottled) {
func.apply(this, args);
isThrottled = true;
setTimeout(() => {
isThrottled = false;
}, delay);
}
};
}
// Example of usage
const throttledFunction = throttle(() => {
console.log("Throttled function executed!");
}, 1000);
// Invokes the throttled function
throttledFunction();
// If throttledFunction is called again within the
// next 1000 milliseconds, the call will be ignored.
Conclusion
As you can see these debounce and throttle functions can be useful in situations where the frequency of events, such as input typing, window resizing or scrolling, needs to be controlled to optimize application performance.