← Back to Chapters

JavaScript Debounce & Throttle

⚡ JavaScript Debounce & Throttle

? Quick Overview

Debounce and throttle are two important techniques to control how often a function is executed in response to frequent events such as scroll, resize, mousemove, or keypress.

They help improve performance and provide a smoother user experience by reducing unnecessary function calls.

⚡ Performance Optimization ? Event Control ?️ Scroll & Input

? Key Concepts

⏳ Debounce

  • Ensures a function runs only after a specified delay has passed since the last call.
  • Ideal when you want the action to happen after the user stops doing something.
  • Common examples: search input, window resize, form validation while typing.

? Throttle

  • Ensures a function runs at most once every given time interval.
  • Ideal when events fire continuously but you want regular, controlled updates.
  • Common examples: scroll position tracking, infinite scroll, mousemove effects.

? When to Use What

  • Use Debounce when you care about the final value or action (e.g., user finished typing).
  • Use Throttle when you care about continuous, but limited updates.

? Syntax & Theory

Both debounce and throttle are usually implemented as higher-order functions: they take a function as input and return a new, wrapped version with extra behavior.

  • They often rely on closures to store timers and timestamps.
  • They use setTimeout, clearTimeout, and sometimes Date.now().
  • The returned wrapper preserves the original function's context and arguments using apply and the rest/spread syntax.

? Code Examples

? Debounce Function Example

? View Code Example
function debounce(func, delay) {
  let timer; // Stores the timeout ID between calls

  return function(...args) {
    clearTimeout(timer); // Reset timer on each call
    timer = setTimeout(() => {
      func.apply(this, args); // Run after user stops triggering events
    }, delay);
  };
}

window.addEventListener('resize', debounce(() => {
  console.log('Resized!'); // Fires only after resizing ends
}, 300));

? Throttle Function Example

? View Code Example
function throttle(func, limit) {
  let lastFunc; // Timeout ID for a scheduled call
  let lastRan; // Timestamp of the last execution

  return function(...args) {
    if (!lastRan) { // First call: run immediately
      func.apply(this, args);
      lastRan = Date.now();
    } else {
      clearTimeout(lastFunc); // Reset any pending execution
      lastFunc = setTimeout(() => {
        const now = Date.now();
        if (now - lastRan >= limit) { // Only run if enough time has passed
          func.apply(this, args);
          lastRan = now;
        }
      }, limit - (Date.now() - lastRan)); // Wait remaining time in the limit window
    }
  };
}

window.addEventListener('scroll', throttle(() => {
  console.log('Scrolling...'); // Throttled scroll handler
}, 500));

? Live Output / Explanation

? How Debounce Behaves

Imagine typing into a search box. With debounce set to 500ms:

  • Every keypress cancels the previous timer.
  • The actual search function runs only once, 500ms after you stop typing.
  • This avoids sending a request for every single keypress.

? How Throttle Behaves

Now imagine a scroll event with throttle set to 200ms:

  • As you scroll, events fire constantly, but the handler runs at most once every 200ms.
  • You still get periodic updates to the scroll position.
  • This keeps the UI responsive and avoids performance bottlenecks.

? Tips & Best Practices

  • Use debounce for actions that should occur after user input has finished (search, resize, validation).
  • Use throttle for actions that should occur continuously but not too often (scrolling, dragging, mousemove effects).
  • Always preserve context and arguments when wrapping functions (use func.apply(this, args)).
  • Pick delays/limits based on UX: 200–500ms is common for input and scroll events.
  • Test in dev tools by watching console logs while interacting with the page.

? Try It Yourself / Practice Tasks

  • Create a search input field and debounce the API call so it only fires 500ms after the user stops typing.
  • Attach a scroll event listener with throttle to log the scroll position every 200ms.
  • Implement both debounce and throttle, then experiment with different delays to see how the behavior changes.
  • Replace console.log with DOM updates (like showing a counter) to visualize how often your functions actually run.