JavaScript Performance

Debouncing vs Throttling

Two of the most important JavaScript performance optimization techniques.

What is Debouncing?

Debouncing delays execution until events stop for a configured pause. It is ideal when only the user's final intent matters.

What is Throttling?

Throttling limits execution frequency while events continue firing. It keeps expensive work predictable during continuous activity.

When should each be used?

Use debounce for search, product lookup, address lookup, and API requests. Use throttle for infinite scroll, resize, mouse tracking, and analytics events.

Search input debounce

Keystrokes Count

0

API Calls Count

0

Saved Requests

0

Pause Behavior

500ms

API executes only after typing stops

Settled

GET /api/products?q=No request yet

Debounce visual timeline

Each keypress resets the timer. The function executes once after the final pause.

1

User Types

2

User Types

3

User Types

4

Waiting...

5

Execute Once

Code Examples

Copy-ready implementations

Basic debounce implementation

Runs once after the user stops triggering the event.

function debounce(fn, delay = 500) {
  let timeoutId;

  return function (...args) {
    clearTimeout(timeoutId);

    timeoutId = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}
Advanced debounce implementation

Adds leading, cancel, and flush behavior for production interfaces.

function debounce(fn, delay = 500, options = {}) {
  let timeoutId;
  let lastArgs;
  let lastThis;
  let result;
  const leading = Boolean(options.leading);

  function invoke() {
    result = fn.apply(lastThis, lastArgs);
    lastArgs = undefined;
    lastThis = undefined;
    return result;
  }

  function debounced(...args) {
    const shouldCallNow = leading && !timeoutId;
    lastArgs = args;
    lastThis = this;

    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      timeoutId = undefined;
      if (!leading) invoke();
    }, delay);

    if (shouldCallNow) return invoke();
    return result;
  }

  debounced.cancel = () => {
    clearTimeout(timeoutId);
    timeoutId = undefined;
  };

  debounced.flush = () => {
    if (!timeoutId) return result;
    clearTimeout(timeoutId);
    timeoutId = undefined;
    return invoke();
  };

  return debounced;
}
Basic throttle implementation

Runs at most once per interval while events continue firing.

function throttle(fn, interval = 500) {
  let lastRun = 0;

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

    if (now - lastRun >= interval) {
      lastRun = now;
      fn.apply(this, args);
    }
  };
}
Advanced throttle implementation

Supports leading and trailing calls so the final event is not lost.

function throttle(fn, interval = 500, options = {}) {
  let lastRun = 0;
  let timeoutId;
  let lastArgs;
  let lastThis;
  const leading = options.leading !== false;
  const trailing = options.trailing !== false;

  function invoke(time) {
    lastRun = time;
    timeoutId = undefined;
    fn.apply(lastThis, lastArgs);
    lastArgs = undefined;
    lastThis = undefined;
  }

  return function throttled(...args) {
    const now = Date.now();
    if (!lastRun && !leading) lastRun = now;

    const remaining = interval - (now - lastRun);
    lastArgs = args;
    lastThis = this;

    if (remaining <= 0 || remaining > interval) {
      if (timeoutId) clearTimeout(timeoutId);
      invoke(now);
      return;
    }

    if (trailing && !timeoutId) {
      timeoutId = setTimeout(() => invoke(Date.now()), remaining);
    }
  };
}
Real World Use Cases
Debouncing: Search Autocomplete
Debouncing: Product Search
Debouncing: Address Lookup
Debouncing: API Requests
Throttling: Infinite Scroll
Throttling: Window Resize
Throttling: Mouse Tracking
Throttling: Analytics Events
Enterprise eCommerce Examples

Product Search

Apply debounce so catalog search, suggestions, pricing, and inventory requests wait for the shopper's final query.

Scroll Tracking

Apply throttle so product listing engagement is measured without flooding analytics pipelines.

Checkout Analytics

Apply throttle so shipping, payment, and validation events stay observable without slowing checkout.

Common Interview Questions

Questions with detailed answers

Q1

What is the difference between debounce and throttle?

Debounce waits for a quiet period and then runs once with the final event. Throttle runs at a fixed maximum frequency while events keep happening. Debounce is about final intent; throttle is about steady, controlled updates.

Q2

When should debounce be used?

Use debounce when intermediate events are noise and only the final value matters. Search autocomplete, product search, address lookup, autosave, and expensive validation are common examples.

Q3

When should throttle be used?

Use throttle when the UI or analytics system needs periodic updates during continuous activity. Infinite scroll, resize handling, mouse tracking, scroll-depth analytics, and checkout telemetry are good fits.

Q4

Can debounce be implemented using throttle?

You can approximate pieces of one with the other, but they solve different timing contracts. A trailing-only throttle may look debounce-like in some cases, yet debounce resets the entire wait window after every event, while throttle preserves a fixed execution cadence.