Limit your function calls with debounce

What is debounce? Debounce is a very interesting and important concept in programming. It triggers a function only once when it is called multiple times in a short period. The most common example is the search input field. Assuming we have an input which triggers a database query whenever the user types anything: const searchProduct = (event) => { // Simulate a search query console.log('Query for:', event.target.value); }; When a user wants to search "Avocado", it will be like: types A -> it queries the database for A types v -> it queries the database for v types o -> it queries the database for o types c -> it queries the database for c and so on. It will eventually make seven database queries. This behaviour is inefficient and can overwhelm the server with unnecessary requests. debounce the calls What we want is that it makes only one query after the whole word is typed. Let's not beat around the bush and see how a debounced version searchProduct can do that: let timer; const debouncedSearchProduct = (event) => { clearTimeout(timer); timer = setTimeout(() => { // Simulate a search query console.log('Query for:', event.target.value); }, 500) }; It will result in like below, only do the database query for one: Let me explain what debouncedSearchProduct does: It delays the database query by 500ms with setTimeout and store the timer's ID into the variable timer If debouncedSearchProduct is called after, it calls clearTimeout(timer) to cancel the timer and do 1. again. As a result, it makes the database query only when the user stops typing longer than 500ms. Of course, you can adjust 500ms to more suitable value. As you can see, debouncing is very simple to achieve. debounce at the beginning The previous example only considers the last event. What if we want to consider the first event and ignore the rest of the events in a timeframe? We can make a few changes to it: let debounced = false; const searchProduct = (event) => { if (debounced) { return } // Simulate a search query console.log('Query for:', event.target.value); debounced = true; setTimeout(() => { debounced = false; }, 500) }; Reuse debounce functionality We can refactor the debounce function to accept a function and return its debounced version so we can reuse it. const searchProduct = (event) => { // Simulate a search query console.log('Query for:', event.target.value); }; function debounce(callback, delay = 500) { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => { callback.apply(this, args); }, delay); }; } const debouncedSearchProduct = debounce(searchProduct, 500); To be honest, we don't need to write our own debounce in practice, unless it's required. For example, we can use debounce method from 3rd-party libraries like lodash const searchProduct = (event) => { // Simulate a search query console.log('Query for:', event.target.value); }; const debouncedSearchProduct = _.debounce(searchProduct, 500); // If you want the leading timeout const debouncedSearchProduct = _.debounce(searchProduct, 500, { leading: true, trailing: false }); Why it's called debounce? Why is it called debounce? You will know that if you have programmed on a hardware button. The digital world is simple, and there are only 0 and 1. Assume there's a button on a screen, it only has two states, unclicked(0) and clicked(1). However, in the analog world, the real world, it is not that simple. For example, When you click a button with your finger, and you hear a "click" sound. The physical button part already vibrates multiple times (at least 20 times a human being can only hear sound bigger than 20Hz) so the sensitive sensor under it may send multiple "clicked" signals. This behaviour is called "bounce" and to remove this effect is called "debounce" Conclusion Although I use Javascript for examples in this article, it can be applied on any languages. And it's not only for UI, you can apply this concept to any kind of programs. For example, don't call ChatGPT API for every changes at the backend to explode your token usage.

Apr 13, 2025 - 18:38
 0
Limit your function calls with debounce

What is debounce?

Debounce is a very interesting and important concept in programming. It triggers a function only once when it is called multiple times in a short period. The most common example is the search input field. Assuming we have an input which triggers a database query whenever the user types anything:

 type="search" oninput="searchProduct(event)" />


When a user wants to search "Avocado", it will be like:

  1. types A -> it queries the database for A
  2. types v -> it queries the database for v
  3. types o -> it queries the database for o
  4. types c -> it queries the database for c
  5. and so on.

It will eventually make seven database queries. This behaviour is inefficient and can overwhelm the server with unnecessary requests.

Make multiple queries

debounce the calls

What we want is that it makes only one query after the whole word is typed. Let's not beat around the bush and see how a debounced version searchProduct can do that:

 type="search" oninput="searchProduct(event)" />


It will result in like below, only do the database query for one:

debounced result

Let me explain what debouncedSearchProduct does:

  1. It delays the database query by 500ms with setTimeout and store the timer's ID into the variable timer
  2. If debouncedSearchProduct is called after, it calls clearTimeout(timer) to cancel the timer and do 1. again.

As a result, it makes the database query only when the user stops typing longer than 500ms. Of course, you can adjust 500ms to more suitable value. As you can see, debouncing is very simple to achieve.

debounce at the beginning

The previous example only considers the last event. What if we want to consider the first event and ignore the rest of the events in a timeframe? We can make a few changes to it:

let debounced = false;

const searchProduct = (event) => {
  if (debounced) { return }

  // Simulate a search query
  console.log('Query for:', event.target.value);

  debounced = true;
  setTimeout(() => {
    debounced = false;
  }, 500)
};

Reuse debounce functionality

We can refactor the debounce function to accept a function and return its debounced version so we can reuse it.

<input type="search" oninput="debouncedSearchProduct(event)" />

<script>
const searchProduct = (event) => {
  // Simulate a search query
  console.log('Query for:', event.target.value);
};

function debounce(callback, delay = 500) {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      callback.apply(this, args);
    }, delay);
  };
}

const debouncedSearchProduct = debounce(searchProduct, 500);
</script>

To be honest, we don't need to write our own debounce in practice, unless it's required. For example, we can use debounce method from 3rd-party libraries like lodash

 type="search" oninput="debouncedSearchProduct(event)" />



Why it's called debounce?

Why is it called debounce? You will know that if you have programmed on a hardware button. The digital world is simple, and there are only 0 and 1. Assume there's a button on a screen, it only has two states, unclicked(0) and clicked(1).

clicking button

However, in the analog world, the real world, it is not that simple. For example, When you click a button with your finger, and you hear a "click" sound. The physical button part already vibrates multiple times (at least 20 times a human being can only hear sound bigger than 20Hz) so the sensitive sensor under it may send multiple "clicked" signals. This behaviour is called "bounce" and to remove this effect is called "debounce"

debounce chart

Conclusion

Although I use Javascript for examples in this article, it can be applied on any languages. And it's not only for UI, you can apply this concept to any kind of programs. For example, don't call ChatGPT API for every changes at the backend to explode your token usage.