JavaScriptMarch 30, 2026

Debounce vs Throttle: When to Use Each

Learn the difference between debounce and throttle and when to use each technique.

Debounce vs Throttle: When to Use Each

Debounce and throttle are essential techniques for optimizing performance. Let’s understand when to use each.

Debounce

Debounce delays execution until after a period of inactivity.

javascript
function debounce(fn, delay) {
  let timeoutId
  return function (...args) {
    clearTimeout(timeoutId)
    timeoutId = setTimeout(() => fn.apply(this, args), delay)
  }
}

// Usage: Search input
const handleSearch = debounce((query) => {
  fetchSearchResults(query)
}, 300)

input.addEventListener('input', (e) => handleSearch(e.target.value))

Use debounce for:

  • Search inputs
  • Window resize handlers
  • Form auto-save
  • Button clicks (prevent double-click)

Throttle

Throttle ensures a function executes at most once per time period.

javascript
function throttle(fn, limit) {
  let inThrottle
  return function (...args) {
    if (!inThrottle) {
      fn.apply(this, args)
      inThrottle = true
      setTimeout(() => inThrottle = false, limit)
    }
  }
}

// Usage: Scroll handler
const handleScroll = throttle(() => {
  updateScrollPosition()
}, 100)

window.addEventListener('scroll', handleScroll)

Use throttle for:

  • Scroll events
  • Mouse move tracking
  • Game loop updates
  • API rate limiting

Visual Comparison

code
Events:    |--x-x-x-x-x-x-x-------x-x-x-x|

Debounce:  |------------------------x----|
           (Fires after last event + delay)

Throttle:  |--x-----x-----x-------x------|
           (Fires at regular intervals)

Advanced: Leading & Trailing

javascript
function debounce(fn, delay, { leading = false, trailing = true } = {}) {
  let timeoutId
  let lastCallTime
  
  return function (...args) {
    const now = Date.now()
    
    if (leading && (!lastCallTime || now - lastCallTime > delay)) {
      fn.apply(this, args)
      lastCallTime = now
    }
    
    clearTimeout(timeoutId)
    
    if (trailing) {
      timeoutId = setTimeout(() => {
        fn.apply(this, args)
        lastCallTime = Date.now()
      }, delay)
    }
  }
}