Skip to main content

Command Palette

Search for a command to run...

Understanding Debounce in JavaScript (With a Real Search Example)

Updated
4 min read
Understanding Debounce in JavaScript (With a Real Search Example)
S

I'm Shubham (@shubhamsinghbundela), I'm a Software Engineer, a Full-stack developer, a tech enthusiast, and a technical writer here on @Hashnode. I have a strong zeal to share my acquired knowledge and I am also willing to learn from others.

When building features like a search bar, calling an API on every keystroke is inefficient.

For example, if a user types:

j → ja → jav → java

Without optimization, the browser would make four API requests.

This increases:

  • Server load

  • Network usage

  • UI lag

To solve this problem, we use debouncing.


What is Debouncing?

Debouncing is a technique that delays function execution until a certain amount of time has passed since the last event.

In simple terms:

The function runs only after the user stops performing an action for a specific time.

A common example is search input fields.

When building a search bar, we should not call the API on every keystroke because that would create too many unnecessary network requests. So, To solve this, we use debouncing. Debouncing ensures that the API call is triggered only after the user stops typing for a specified amount of time.

Basic Debounce Implementation

Here is a simple debounce function:

function debounce(fn, delay) {
  let timerId;

  return function (...args) {
    clearTimeout(timerId); // cancel previous scheduled execution

    timerId = setTimeout(() => {
      fn(...args);
    }, delay);
  };
}

Example usage:

const search = (query) => {
  console.log("Searching for", query);
};

const searchWithDebounce = debounce(search, 1000);
searchWithDebounce("j");
searchWithDebounce("ja");
searchWithDebounce("jav");
searchWithDebounce("java");

How It Works

Suppose the debounce delay is 1 second.

User typing timeline:

Time User Input Action
0ms j timer started
300ms ja previous timer cleared
600ms jav timer cleared again
900ms java timer cleared again
1900ms user stops typing search("java") runs

Only one API call happens.

The key line is:

clearTimeout(timerId);

This cancels the previous scheduled function execution and starts a new timer.

So the timer keeps resetting until the user stops typing.


The Real Problem With API Calls

Debouncing solves too many API calls, but another problem can occur.

Consider this situation:

Two searches happen:

search("first")
search("second")

But the API response times are different.

Example:

first  → response in 100ms
second → response in 20ms

The second request finishes first.

Then the first request finishes later, which can overwrite the UI with outdated results.

This is called a stale response problem.


Debounce With Result Protection

To fix this, we track the latest request.

function createSmartDebounce(worker, waitMs) {
  let timer = null;
  let latestRequestId = 0;

  return function (...args) {
    const callback = args.pop();

    latestRequestId++;
    const requestId = latestRequestId;

    clearTimeout(timer);

    timer = setTimeout(() => {
      worker(...args, (err, data) => {

        // Ignore stale responses
        if (requestId !== latestRequestId) {
          return;
        }

        callback(err, data);
      });
    }, waitMs);
  };
}

Worker simulation:

const worker = (input, cb) => {
  const delay = input === "first" ? 100 : 20;

  setTimeout(() => {
    cb(null, input);
  }, delay);
};

Example Scenario

debounced("first");
debounced("second");

Timeline:

Time Event
0ms first request triggered
60ms second request triggered
130ms second response arrives
150ms first response arrives

Without protection:

UI shows: first ❌

With request ID protection:

UI shows: second ✅

Older responses are ignored.


Key Takeaways

Debouncing helps to:

  • Reduce unnecessary API calls

  • Improve performance

  • Improve user experience

However, when dealing with async requests, you must also protect against stale responses.

A robust solution includes:

1️⃣ Debounce timer
2️⃣ Request tracking
3️⃣ Ignoring outdated responses


Final Mental Model

Think of debounce like a resettable countdown timer:

The function executes only when the user stops interacting for the specified time.

More from this blog

Shubham Tech. Blog's

55 posts

Problem Solver | Currently Working As a Full Stack Developer, Community Leader At @Dev_Matrix | Previously Contributor at @RealDevSquad, @TeamShiksha