Hiprup

How do you implement debounce from scratch?

Debounce delays running a function until a pause in activity, so it fires once after the calls stop.

The approach:

  1. Keep a timer variable in a closure.

  2. On each call, clear the previous timer.

  3. Set a new timer for the delay; when it fires, run the function.

  4. Preserve this and arguments so the original call's context is used.

Result: rapid calls reset the clock; the function runs only once, after the final call.

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

// Usage
const search = debounce((query) => {
  console.log('Searching for:', query);
  fetch(`/api/search?q=${query}`);
}, 300);

input.addEventListener('input', (e) => search(e.target.value));
// Only searches after user stops typing for 300ms

// With leading option (fire immediately, then debounce)
function debounceLeading(fn, delay) {
  let timer;
  return function(...args) {
    const callNow = !timer;
    clearTimeout(timer);
    timer = setTimeout(() => { timer = null; }, delay);
    if (callNow) fn.apply(this, args);
  };
}

// Cancel support
function debounceWithCancel(fn, delay) {
  let timer;
  const debounced = function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
  debounced.cancel = () => clearTimeout(timer);
  return debounced;
}

Each call clears the previous timer (clearTimeout) and starts a new one (setTimeout). The function only fires when the timer completes without being cleared — meaning the user stopped triggering events for the full delay. fn.apply(this, args) preserves context and arguments.

The leading variant fires immediately on first call, then debounces. Cancel allows external cancellation.

This is the #1 most asked JS coding question. Know the basic version (clearTimeout + setTimeout), the leading variant (fire first, then debounce), and the cancel method. fn.apply(this, args) for context preservation is important.

Show usage with a search input.

How do you implement debounce from scratch? | Hiprup