Concepts/JavaScript

Debounce vs Throttle

Both limit how often a function executes, but debounce waits until activity stops while throttle enforces a steady maximum rate.

Side-by-Side Comparison

FeatureDebounceThrottle
Execution timingAfter activity stopsAt regular intervals during activity
During rapid callsKeeps resetting, only last firesFires once per interval
Guaranteed executionOnly after pauseAt steady intervals
First callDelayedImmediate (leading edge)

Code Examples

Debounce

  • Delays execution until after a pause in activity
  • Resets the timer on every new call
  • Only the LAST call in a burst actually executes
  • Best for "wait until the user is done" scenarios
function debounce(fn, delay) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => fn(...args), delay)
  }
}

// User types "hello" — only fires ONCE after typing stops
input.addEventListener('input',
  debounce(handleSearch, 300)
)

Throttle

  • Executes at most once per time interval
  • Guarantees regular execution during sustained activity
  • First call executes immediately (leading edge)
  • Best for "limit the rate" scenarios
function throttle(fn, limit) {
  let inThrottle = false
  return (...args) => {
    if (!inThrottle) {
      fn(...args)
      inThrottle = true
      setTimeout(() => inThrottle = false, limit)
    }
  }
}

// Fires at most every 200ms during scrolling
window.addEventListener('scroll',
  throttle(handleScroll, 200)
)

When to Use Which

Debounce

Search input, form validation, window resize handler, auto-save — anything where you want to wait until the user finishes.

Throttle

Scroll handlers, mousemove tracking, rate-limited API calls, game input — anything where you need regular updates during continuous activity.

Common Mistakes

Interview Questions

What happens if you debounce a scroll handler?

The handler only fires after the user stops scrolling, which creates a poor UX — no visual updates during scrolling, then a sudden jump. Throttle is the right choice for scroll because it provides regular updates at a controlled rate.

Implement debounce from scratch

Return a wrapper function that clears any existing timeout and sets a new one. The key insight is that clearTimeout(timer) on each call resets the delay, so only the last call in a burst survives to execute.

How do leading and trailing edge throttle differ?

Leading edge executes on the first call then blocks. Trailing edge blocks then executes at the end. Lodash's throttle supports both via options: { leading: true, trailing: true }.

Deep Dive