Marcell Ciszek Druzynski

Debounce in JavaScript

Debounce is an valuable technique for optimising code in our programs. By employing debounce, we can ensure that our code is triggered in a controlled manner.

June 25, 2023

Debounce is a valuable technique for optimising code in our programs. By employing debounce, we can ensure that our code is triggered in a controlled manner.

Metaphors for debounce

  • Debounce as a traffic controller: Imagine the debounce function as a traffic controller at a busy intersection. It intelligently manages the flow of cars, ensuring that one car passes through at a time. Similarly, debounce regulates the execution of code, allowing it to trigger once within a specified timeframe, preventing an overwhelming influx of requests.
  • Debounce as a Sniper's Perfect Shot: Imagine debounce as a skilled sniper taking a perfectly timed shot. The sniper waits patiently, tracking the target's movements, and carefully takes aim, ensuring the perfect moment to fire. Similarly, debounce waits for the opportune time, ensuring that the code execution is triggered when necessary, optimising performance and accuracy.

The search problem

Consider a search field where the user can enter a search term. Instead of displaying the results after each keystroke, we want to wait until the user has finished typing. This approach is crucial to address potential performance issues that may arise.

Imagine the impact on performance if we were to execute a database query for every single keystroke, including new letters, spaces, and backspaces. To mitigate this problem, we can leverage debounce to improve the situation.

Using debounce, we delay the execution of the database query until the user has completed typing, allowing us to optimise the process.

Let's go over a example where we use debounce to optimise our code.

1searchNormal.addEventListener("input", search);
2searchDebounce.addEventListener("input", debounce(search, 400));
3
4function search(e) {
5 const value = e.target.value.trim().toLowerCase();
6 const foundItems = randomItems.filter((item) =>
7 item.toLowerCase().includes(value),
8 );
9 if (value && foundItems.length > 0) {
10 result.innerHTML = foundItems
11 .map((item) => `<li class="item">${item}</li>`)
12 .join("");
13 } else {
14 result.innerHTML = `<li class="item">No results found</li>`;
15 }
16}
17
18// Debounce
19function debounce(cb, time = 100) {
20 let timeout;
21 return (...args) => {
22 clearTimeout(timeout);
23 timeout = setTimeout(() => {
24 cb(...args);
25 }, time);
26 };
27}
1searchNormal.addEventListener("input", search);
2searchDebounce.addEventListener("input", debounce(search, 400));
3
4function search(e) {
5 const value = e.target.value.trim().toLowerCase();
6 const foundItems = randomItems.filter((item) =>
7 item.toLowerCase().includes(value),
8 );
9 if (value && foundItems.length > 0) {
10 result.innerHTML = foundItems
11 .map((item) => `<li class="item">${item}</li>`)
12 .join("");
13 } else {
14 result.innerHTML = `<li class="item">No results found</li>`;
15 }
16}
17
18// Debounce
19function debounce(cb, time = 100) {
20 let timeout;
21 return (...args) => {
22 clearTimeout(timeout);
23 timeout = setTimeout(() => {
24 cb(...args);
25 }, time);
26 };
27}

The debounce function ensures that a certain action (in this case, calling the search function) is not triggered immediately and excessively, but after a certain period of inactivity.

Here's a breakdown of how the debounce function works:

  1. It takes two parameters: a callback function (cb) and a time value (defaulted to 100 milliseconds).
  2. Inside the function, a variable named timeout is declared but not assigned a value yet.
  3. The debounce function returns another function. Think of it as a "wrapper" function that adds some behaviour to the original callback function.
  4. When the returned function is called (e.g., in the line searchDebounce.addEventListener("input", debounce(search, 400));), it performs the following steps:
    • It clears any existing timeout that might have been set previously using clearTimeout(timeout). This is like telling your friend to wait a bit longer before bombarding you with more questions.
    • It sets a new timeout using setTimeout. This timeout will wait for the specified time (in this case, 400 milliseconds) before executing the callback function (cb).
    • The callback function (cb) is called within the timeout. This is like finally allowing your friend to ask you a question after they have waited patiently for a certain period of time.

By using the debounce function in this code, the search function is wrapped in a way that it won't be called immediately every time the input event occurs. Instead, it will be called only after a certain delay (400 milliseconds in this case) of no further input events. This helps to prevent excessive and unnecessary calls to the search function, optimising the performance of the search feature.

Debounce allows us to delay the execution of the database query until the user has finished typing. Instead of immediately sending a request for every keystroke, we wait for a short period of inactivity. This way, if the user continues typing, we reset the timer and wait again. Only when the user pauses for a moment, the query is finally executed.

By using debounce, we improve the performance of our code and prevent unnecessary database calls. It's like having a smart system that waits patiently for the user to finish typing before performing the heavy lifting, ensuring a smoother and more efficient experience

You can view the code example codepen.

Libraries with debounce

Some handful libraries where we can use debounce for vanilla JS, React and other frameworks.