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.

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)" />
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 forA
- types
v
-> it queries the database forv
- types
o
-> it queries the database foro
- types
c
-> it queries the database forc
- 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:
type="search" oninput="searchProduct(event)" />
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 variabletimer
- If
debouncedSearchProduct
is called after, it callsclearTimeout(timer)
to cancel the timer and do1.
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)" />
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.