What Is Debouncing?

Debouncing means waiting until a user has stopped doing something before executing a function. If a user types in a search box and you search on every keystroke, you'll fire dozens of API calls. With debouncing, you wait until they pause typing (e.g. 300ms of inactivity) then fire once.

Basic Debounce Function

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

How it works: every call clears the previous timer and starts a new one. The wrapped function only runs when the timer completes without being reset.

Usage Examples

// Search input — wait 300ms after user stops typing
const searchInput = document.getElementById('search');

const handleSearch = debounce(function(e) {
  const query = e.target.value;
  fetchResults(query); // API call
}, 300);

searchInput.addEventListener('input', handleSearch);
// Window resize — wait 200ms after resize stops
const handleResize = debounce(function() {
  recalculateLayout();
}, 200);

window.addEventListener('resize', handleResize);
// Save draft automatically
const editor = document.getElementById('editor');

const saveDraft = debounce(async function() {
  const content = editor.value;
  await fetch('/api/save-draft', {
    method: 'POST',
    body: JSON.stringify({ content }),
    headers: { 'Content-Type': 'application/json' }
  });
  console.log('Draft saved');
}, 1000); // save 1 second after user stops typing

editor.addEventListener('input', saveDraft);

Debounce vs Throttle

TechniqueBehaviourBest For
DebounceFires once after activity stopsSearch inputs, form validation, save drafts
ThrottleFires at most once per intervalScroll events, mouse move, game loops
// Throttle implementation
function throttle(fn, interval) {
  let lastTime = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

// Throttle scroll handler — fires max once per 100ms
window.addEventListener('scroll', throttle(updateScrollProgress, 100));

Using a Library

If you're already using Lodash, use its optimised implementations:

import { debounce, throttle } from 'lodash';

const handleSearch = debounce((e) => fetchResults(e.target.value), 300);
const handleScroll = throttle(() => updateNav(), 100);