🧠 Understanding Debounce and Throttle in JavaScript (Made Super Simple!)

Yash GroverYash Grover
4 min read

When building modern web apps, performance matters β€” a lot. If you're handling events like scroll, resize, or input, you might accidentally run the same function hundreds of times in a second! That’s where debounce and throttle come in.

Let’s break these two down step by step, using real code and even a pizza shop analogy πŸ• to help it all make sense.

πŸ’‘ Simple Definitions

Debounce:

Debounce waits for a pause β€” it runs your function after the event has stopped firing for a certain amount of time.

Throttle:

Throttle limits how often a function can run β€” it ensures the function runs at most once every N milliseconds, no matter how many times the event happens.

Confused ? Let’s try to understand via an example !

πŸ“¦ Code Examples (How They Work)

βœ… Debounce Example:

window.addEventListener('resize', debounce(() => {
  console.log('Resize event handler called!');
}, 300));

πŸ“ Output:
When you resize the window, this only logs once β€” after you stop resizing for 300ms. It won’t spam the console while you're resizing.


βœ… Throttle Example:

window.addEventListener('scroll', throttle(() => {
  console.log('Scroll event handler called!');
}, 200));

πŸ“ Output:
Even if you scroll like crazy, this logs the message at most once every 200ms. It keeps things smooth and avoids performance bottlenecks.

Still confused ? Let’s try to understand via an exciting analogy !

πŸ• Real-Life Analogy: The Pizza Ordering Receptionist

Let’s imagine a pizza shop with a very busy receptionist. She’s trying to manage customer calls.

Debounce:

The receptionist waits until calls stop coming in for 5 minutes. Only then, she processes the last caller's order. If a new call comes in during those 5 minutes, she resets the timer and waits again.

πŸ“ž... (wait)... πŸ“ž... (wait again)... silence... βœ… Now take the last order.

Great when: You want to wait for the user to finish their input (like typing a search term) before doing something.


Throttle:

The receptionist picks up only one call every 5 minutes, no matter how many people call. She tells everyone else to wait. Even if 10 people call, she only takes 1 order every 5 minutes.

πŸ“ž βœ… Order taken. ⏳ Waiting... πŸ“žπŸ“žπŸ“ž ❌ (Ignored)... πŸ“ž βœ… Next order.

Great when: You want regular updates (like scroll or mouse move) without overloading the system.

πŸ” Debounce vs Throttle (Quick Comparison)

FeatureDebounceThrottle
Function RunsAfter a delay of no activityAt most once every fixed interval
Common Use CaseTyping in search bars, resizing windowsScrolling, mouse movement, window resizing (for animation)
BehaviorDelays function until user stops doing somethingAllows function to run at intervals during activity
VisualπŸ•’ Wait until calm, then act🚦 Act at green light intervals only

πŸ§‘β€πŸ’» Write Your Own Debounce and Throttle in JavaScript

Let's now build our own versions of debounce and throttle β€” this helps you understand how they work under the hood.


πŸ› οΈ Debounce: Our Own Version

function debounce(func, delay) {
  let timerId;

  return function (...args) {
    const context = this;

    clearTimeout(timerId);

    timerId = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  };
}

βœ… How it works:

  • timerId stores the reference to the timeout.

  • Every time the function is called, we clear the previous timer.

  • We wait delay milliseconds since the last call, then execute the function.


πŸ› οΈ Throttle: Your Own Version

function throttle(func, limit) {
  let lastCalled = 0;

  return function (...args) {
    const context = this;
    const now = Date.now();

    if (now - lastCalled >= limit) {
      lastCalled = now;
      func.apply(context, args);
    }
  };
}

βœ… How it works:

  • We store the time when the function was last called.

  • Each time the event fires, we check if enough time has passed.

  • If yes, we run the function and update lastCalled.


πŸ€” What’s ...args and context?

...args

This collects all arguments passed to your throttled or debounced function so you can forward them to the real function later.

debouncedSearch("hello", "user");

...args becomes: ["hello", "user"]


context (this)

Sometimes, your function might depend on the object it belongs to (like a method on an object). If we don’t preserve this, we can lose context.

That’s why we do:

const context = this;
func.apply(context, args);

This ensures the original function is called in the correct context with the right arguments.

🎯 Final Thoughts

Debounce and throttle are small tools that solve big performance problems. Whether you’re building a live search, lazy-loading images, or optimizing a scroll-heavy UI β€” these two patterns are your best friends.

0
Subscribe to my newsletter

Read articles from Yash Grover directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Yash Grover
Yash Grover