Have you ever typed into a search box and noticed that suggestions don't appear immediately, but only after you pause for a moment?, or you resized your browser window and observed that complex layout recalculations don't happen continuously, but only after you stop dragging?, that's debouncing in action. Debouncing is a powerful technique that prevents excessive function calls during rapid user interactions, dramatically improving performance and user experience.
In modern web applications, events like keystrokes and mouse movements can fire hundreds of times per second. Without proper control, each event could trigger expensive operations like API calls and DOM manipulations, overwhelming your application and creating a sluggish user experience. Debouncing intelligently solves this problem by delaying these events from running until the user has finished their action.
You will get to learn more about debouncing, and how to implement it with JavaScript.
A Problem with Web Apps
Modern web applications are event-driven, so every user interaction such as typing in an input field, clicking a button, scrolling through content etc. runs a certain type of event your application responds to. While this event-driven architecture enables rich, interactive experiences, it also creates a problem called event flooding. This is a scenario where events fire rapidly, overwhelming the single-threaded event loop in JavaScript, and degrading browser performance.
Let's consider an example: A fashion e-commerce store
Assuming a user types "Tank tops" in the search bar of this store's website, each character they type causes the event to be fired 9 good times. If the application is making an API call on every keystroke to fetch suggestions, that means we've also sent 9 HTTP requests to the server. The issue here is that the first 4 requests made to the API is unnecessary because the user is still typing, and by the time those results even come back, they are already outdated.
This same problem also occurs with other types of events such as resizing the window, scrolling through a page, moving the mouse or even validating a form as the user types. Without implementing a way to control how often these events are fired, the performance of your application will be heavily affected, wasting computational resources and also overwhelming your server with unnecessary API calls. It was for this reason debouncing was created.
What is Debouncing?
Debouncing is a technique that controls how often a function executes. This concept was taking from the field of electronics and was adopted into web development by John Hann. Debouncing ensures that an event is not immediately fired off each time a user performs an action, but rather after a certain period of time has elapsed. This is the step by step process of how Debouncing works:
- The user triggers an event (e.g. typing a character in an input field)
- The debounced function starts a timer
- The user triggers another event before the timer expires (types another character into the input field)
- As a result, the timer is cancelled and resets
- Steps 3 & 4 keep repeating as long the event keeps firing
- Finally, the user stops triggering the event
- The timer completes and the function executes
A Real-World Analogy: An Elevator Door
Imagine you're entering an elevator, you press the button to hold the door open for your colleague who's approaching, the elevator door stays open, your colleague arrives and presses the "door open" button again because they see another person coming, the door remains opened and the timer resets. This process continues, and each press of the "door open" button (i.e. the debounced function being called) resets the timer. Only when everyone stops pressing the button and a certain amount of time passes without any new presses does the elevator door finally close (the function executes). That's exactly how debouncing works: continuous interactions keep resetting the timer, and the actual function only executes during a pause in the activity.
Debouncing vs Throttling
When talking about Debouncing, you might come across another concept that is similar called Throttling, but they are not to be confused with each other. Debouncing delays a function from executing until after a certain period of inactivity, while Throttling limits the function execution to once per specified time interval, regardless of how many times the event fires. In throttling, the function executes at regular intervals while events are firing.
Debouncing behavior
User types: a-b-c-d-e [stops typing]
Timeline: -----a-b-c-d-e-----------[EXECUTE]
// Events keep resetting the timer → Execute after a period of no activity
Throttling behavior
User types: a-b-c-d-e-f-g-h-i-j
Timeline: [EXECUTE]---e---[EXECUTE]---j
// Execute immediately → Wait interval → Execute again
Debouncing executes a function after things have calm down, while Throttling executes a function at most once per time period, whether things calm down or not. Since this article is not about Throttling, I won't go into it, possibly in another article 😁.
Debouncing Example
As earlier explained, debouncing shines in scenarios where you need to wait for a pause in the users activity before taking action e.g. after they finish typing into an input field. Let's see an example using a simple weather application where the user types their location, and the app fetches data about that location. This will further enables us see the difference between using Debouncing and not using it.
Without Debouncing
In this scenario, as the user types into the input field, we immediately start hitting the backend to fetch data.
JavaScript code:
const input = document.querySelector("input");
const APP_ID = "xxxxxxxxx";
input.addEventListener("keyup", (e) => {
fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${input.value}&units=metric&appid=${APP_ID}`,
)
.then((res) => res.json())
.then((data) => {
console.log(data);
});
});
As you can see, we keep calling the API each time the user types into the input field. The first 3 - 4 set of results don't return anything meaningful from the API until we type enough characters to fetch a meaningful result. For this simple application, this isn't really a problem, but you can imagine how this can be problematic by the time the app becomes more complex or we are dealing with a larger application, the performance will be drastically affected.
Let's see how we can handle this better with Debouncing.
With Debouncing
In this scenario, as the user types into the input field, we wait for 1s before sending the request to the backend to fetch data.
JavaScript code:
const input = document.querySelector("input");
const APP_ID = "xxxxxxxxx";
function fetchResults() {
fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${input.value}&units=metric&appid=${APP_ID}`,
)
.then((res) => res.json())
.then((data) => {
console.log(data);
});
}
function debounceSearchInput(func, delay) {
let timeoutID;
return function () {
if (timeoutID) {
clearTimeout(timeoutID);
}
timeoutID = setTimeout(() => {
func();
}, delay);
};
}
input.addEventListener("keyup", debounceSearchInput(fetchResults, 1000));
From this example, you can see that until we are done typing, the application will not send any request to the server to fetch data until after a period of time has passed when we don't type anything into the input field again. This is much more efficient and allows the server only return useful data back to us. When building production-grade applications with events that shouldn't fire immediately, you will typically use Debouncing to handle that.
The idea that makes debouncing work is a concept called Closures. If you look at the code above, you would realize that we are already calling the debounceSearchInput() function even before the event fires, so how come the logic still works?, that's where closures come in. I won't dive into what closures are, but if you don't know about them, and would like to be enlightened, I wrote an article about it, I can assure you it will help you understand better what's going on here.
Conclusion
This is basically what debouncing is about, and how you can use it in your web applications. You can also extend this and apply it with any kind of language you use to build web applications, the idea is really the same. If you would like to see how Debouncing works in React as well, do let me know, I am thinking of writing an article on it.


Top comments (0)