DEV Community

Cover image for Debouncing vs Throttling: The Difference Every Developer Should Know
Tanu Priya
Tanu Priya

Posted on

Debouncing vs Throttling: The Difference Every Developer Should Know

A user types into a search box:

R
Re
Rea
Reac
React
Enter fullscreen mode Exit fullscreen mode

If your application sends an API request for every keystroke, that's 5 requests for a single search.

Now consider scrolling through a webpage. The browser can fire scroll events continuously while the user moves.

Should your application run expensive logic for every single event?

Probably not.

This is where two simple but extremely useful techniques come in:

Debouncing and Throttling.

They both help control how often a function executes, but they solve different problems.

The easiest way to remember them is:

Debounce waits for the activity to stop. Throttle limits how often the activity is processed.

Let's see what that actually means.


The Problem: Events Can Happen Too Fast

Modern applications react to events constantly:

Keyboard input
Mouse movement
Scrolling
Window resizing
API responses
Button clicks
Enter fullscreen mode Exit fullscreen mode

Some of these events can happen much faster than your application needs to process them.

For example, a user typing:

React
Enter fullscreen mode Exit fullscreen mode

might generate:

R      → event
Re     → event
Rea    → event
Reac   → event
React  → event
Enter fullscreen mode Exit fullscreen mode

But perhaps you only want to search after the user has finished typing.

That's a perfect use case for debouncing.

On the other hand, when the user scrolls, you may want updates to happen continuously — just not hundreds of times per second.

That's where throttling helps.


What Is Debouncing?

Debouncing means:

Don't execute the function until the user stops triggering the event for a specified amount of time.

Imagine a search box with a debounce delay of 300ms.

The user types:

R
Enter fullscreen mode Exit fullscreen mode

Timer starts.

Then:

Re
Enter fullscreen mode Exit fullscreen mode

The timer resets.

Then:

Rea
Enter fullscreen mode Exit fullscreen mode

Timer resets again.

This continues until the user stops typing.

Finally:

React
   ↓
300ms without typing
   ↓
API request
Enter fullscreen mode Exit fullscreen mode

Instead of:

R      → API
Re     → API
Rea    → API
Reac   → API
React  → API
Enter fullscreen mode Exit fullscreen mode

you get:

React → API
Enter fullscreen mode Exit fullscreen mode

That's the core idea behind debouncing.


A Simple Debounce Implementation

function debounce(fn, delay) {
  let timer;

  return (...args) => {
    clearTimeout(timer);

    timer = setTimeout(() => {
      fn(...args);
    }, delay);
  };
}
Enter fullscreen mode Exit fullscreen mode

Now:

const search = debounce((query) => {
  console.log("Searching:", query);
}, 300);
Enter fullscreen mode Exit fullscreen mode

If we call:

search("R");
search("Re");
search("Rea");
search("Reac");
search("React");
Enter fullscreen mode Exit fullscreen mode

the previous timer keeps getting cancelled.

Only after 300ms of inactivity does the function execute.


Where Is Debouncing Useful?

Debouncing works best when intermediate events don't matter and you mainly care about the final action.

Search

User types
   ↓
Wait
   ↓
Search
Enter fullscreen mode Exit fullscreen mode

Autosave

User edits document
   ↓
Wait for pause
   ↓
Save
Enter fullscreen mode Exit fullscreen mode

Form validation

User types
   ↓
Wait
   ↓
Validate
Enter fullscreen mode Exit fullscreen mode

Filtering

User changes filters
   ↓
Wait
   ↓
Perform expensive filtering
Enter fullscreen mode Exit fullscreen mode

The common pattern is:

Event Event Event Event
          ↓
       STOP
          ↓
       Execute
Enter fullscreen mode Exit fullscreen mode

What Is Throttling?

Throttling takes a different approach.

Instead of waiting for the activity to stop, throttling says:

Execute the function at most once during a specific time interval.

Suppose we throttle a function to once every 200ms.

The browser might generate:

scroll scroll scroll scroll scroll scroll scroll
Enter fullscreen mode Exit fullscreen mode

But our application processes it like:

0ms     → Execute
50ms    → Ignore
100ms   → Ignore
150ms   → Ignore
200ms   → Execute
250ms   → Ignore
300ms   → Ignore
400ms   → Execute
Enter fullscreen mode Exit fullscreen mode

The event is still happening continuously.

We're simply controlling how frequently our expensive logic runs.


A Simple Throttle Implementation

function throttle(fn, delay) {
  let lastCall = 0;

  return (...args) => {
    const now = Date.now();

    if (now - lastCall >= delay) {
      lastCall = now;
      fn(...args);
    }
  };
}
Enter fullscreen mode Exit fullscreen mode

For example:

const handleScroll = throttle(() => {
  console.log("Processing scroll...");
}, 200);

window.addEventListener("scroll", handleScroll);
Enter fullscreen mode Exit fullscreen mode

Even if the browser fires many scroll events, our function runs at a controlled frequency.


Where Is Throttling Useful?

Throttling is useful when you do want updates while the activity is happening, but you don't need to process every event.

Common examples include:

Scroll events

Scroll
  ↓
Periodic updates
Enter fullscreen mode Exit fullscreen mode

Mouse movement

Mouse moves
  ↓
Process position periodically
Enter fullscreen mode Exit fullscreen mode

Window resizing

Resize
  ↓
Recalculate periodically
Enter fullscreen mode Exit fullscreen mode

Dragging

Drag
  ↓
Update position at a controlled rate
Enter fullscreen mode Exit fullscreen mode

The pattern is:

Event Event Event Event Event Event
 ↓          ↓          ↓
Run       Run        Run
Enter fullscreen mode Exit fullscreen mode

Debounce vs Throttle

Here's the simplest comparison:

Debouncing Throttling
Main idea Wait for activity to stop Limit execution frequency
During continuous activity Doesn't execute yet Executes periodically
Search box ✅ Great fit Usually unnecessary
Scroll Usually unnecessary ✅ Great fit
Autosave ✅ Great fit Sometimes
Mouse movement Usually unnecessary ✅ Great fit
Window resize Sometimes ✅ Common
Final event matters Not necessarily

The mental model is more important than memorizing the table.

Debounce

Activity
Activity
Activity
Activity
   ↓
STOP
   ↓
WAIT
   ↓
EXECUTE
Enter fullscreen mode Exit fullscreen mode

Throttle

Activity Activity Activity Activity Activity
    ↓             ↓             ↓
  EXECUTE       EXECUTE       EXECUTE
Enter fullscreen mode Exit fullscreen mode

A Real Search Example

Imagine you're building an e-commerce application.

The search box receives:

wireless headphones
Enter fullscreen mode Exit fullscreen mode

Without debouncing, the application might make a request for almost every character:

w
wi
wir
wire
wirel
wirele
...
wireless headphones
Enter fullscreen mode Exit fullscreen mode

That's a lot of unnecessary work.

With debouncing:

User types
    ↓
Timer resets after every keystroke
    ↓
User stops typing
    ↓
300ms passes
    ↓
Search API
Enter fullscreen mode Exit fullscreen mode

The application waits for a meaningful pause before doing the expensive operation.

This isn't just about saving API calls.

It can also mean:

  • Less server work
  • Less network traffic
  • Fewer unnecessary renders
  • Better user experience

A Real Scroll Example

Now consider a dashboard where you want to update something based on scroll position.

The browser may produce many events while scrolling:

scroll
scroll
scroll
scroll
scroll
scroll
scroll
...
Enter fullscreen mode Exit fullscreen mode

Waiting for the user to stop scrolling isn't what we want.

We want the application to keep responding while scrolling.

But processing every event may be unnecessarily expensive.

So we throttle:

Scroll events
      ↓
   Throttle
      ↓
Process every 100–200ms
      ↓
Update UI
Enter fullscreen mode Exit fullscreen mode

That's why throttling is generally a better fit for continuous interactions.


The Question You Should Ask

When you encounter a rapidly firing event, don't start by asking:

"Should I use debounce?"

Instead ask:

Do I care about the final action?

If yes:

→ Debounce
Enter fullscreen mode Exit fullscreen mode

For example:

Search
Autosave
Validation
Enter fullscreen mode Exit fullscreen mode

Do I need periodic updates while the activity continues?

If yes:

→ Throttle
Enter fullscreen mode Exit fullscreen mode

For example:

Scroll
Mouse movement
Dragging
Resize
Enter fullscreen mode Exit fullscreen mode

That's the distinction.


What About React?

You'll frequently encounter debounce and throttle in React applications.

For example:

function SearchBox() {
  const handleSearch = debounce((query) => {
    fetchResults(query);
  }, 300);

  // ...
}
Enter fullscreen mode Exit fullscreen mode

However, there's an important React-specific consideration.

Components can render many times, and functions created during rendering can also be recreated.

So blindly creating a new debounced or throttled function during every render can lead to unexpected behavior.

In real React applications, you need to think about:

  • Function identity
  • Cleanup
  • Dependencies
  • Component lifecycle
  • Cancelling pending work

Depending on the situation, tools such as useMemo, useCallback, useRef, or custom hooks can help manage this correctly.

The important thing is not to memorize a particular React pattern.

Understand what you're trying to control first.


Don't Use Them Everywhere

Debouncing and throttling are useful, but they aren't performance magic.

Adding them to every event can actually make your code harder to understand.

If an operation is cheap:

button.addEventListener("click", () => {
  console.log("Clicked");
});
Enter fullscreen mode Exit fullscreen mode

there's probably no reason to throttle it.

Similarly, if the user expects an immediate response, adding a debounce delay could make the interface feel slower.

The goal isn't:

"Use debounce or throttle whenever an event happens frequently."

The goal is:

Match the frequency of your work to what the application actually needs.


One More Important Difference

There's another way to think about the two.

Debouncing changes when the work happens.

It says:

"Wait until things calm down."

Typing → Typing → Typing → STOP
                              ↓
                           Execute
Enter fullscreen mode Exit fullscreen mode

Throttling changes how often the work happens.

It says:

"The activity can continue, but I'm only going to process it at this rate."

Scroll → Scroll → Scroll → Scroll → Scroll
   ↓                    ↓
 Execute              Execute
Enter fullscreen mode Exit fullscreen mode

Once you see it this way, the difference becomes much easier to remember.


The Bigger Engineering Lesson

Debouncing and throttling are really about controlling unnecessary work.

A browser can generate events faster than your application needs to process them.

That doesn't mean the browser is doing something wrong.

It means your application needs to decide:

How often does this work actually need to happen?

For a search box, you probably don't need to search after every keystroke.

For scrolling, you probably don't need to run expensive calculations for every single event.

That's where these techniques become useful.


The 10-Second Cheat Sheet

SEARCH BAR
User types
    ↓
Wait until they stop
    ↓
DEBOUNCE
Enter fullscreen mode Exit fullscreen mode
SCROLL
User keeps scrolling
    ↓
Process periodically
    ↓
THROTTLE
Enter fullscreen mode Exit fullscreen mode

Or simply remember:

Debounce = "Wait for the pause."

Throttle = "Control the pace."

The real skill isn't knowing the definitions.

It's recognizing when the application doesn't need to react to every single event.

And that's a small optimization that can make a surprisingly big difference in real-world applications.

Top comments (0)