# Understanding Debounce in JavaScript (With a Real Search Example)

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

For example, if a user types:

```plaintext
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:

```javascript
function debounce(fn, delay) {
  let timerId;

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

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

Example usage:

```javascript
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:

```javascript
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:

```plaintext
search("first")
search("second")
```

But the API response times are different.

Example:

```plaintext
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**.

* * *

### Solution: Debounce

```javascript
function createSmartDebounce(worker, waitMs) {
  let timerId;
  return function (...args) {
    clearTimeout(timerId);
    timerId = setTimeout(() => {
      worker(...args);
    }, waitMs);
  };
}
```

Worker simulation:

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

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

### Example Scenario

```plaintext
debounced("first");
debounced("second");
```

Timeline:

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

* * *

## 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**.
