All Snippets Snippet JavaScript

Debounce Function

Events Exec wait...

Delay function execution until the user stops triggering events, preventing excessive API calls and layout recalculations.

D
Kunwar "AKA" AJ Sharing what I have learned
Feb 21, 2026 2 min JavaScript

The Pattern

debounce.js
function debounce(fn, delayMs) {
    let timerId = null;

    return function (...args) {
        // Every call resets the timer
        clearTimeout(timerId);

        timerId = setTimeout(() => {
            fn.apply(this, args);
        }, delayMs);
    };
}

What Happens Under the Hood

Let us trace what happens when a user types “hello” in a search box with a 300ms debounce:

execution-flow.txt
User types "h"     → Timer starts (300ms countdown)
User types "e"     → Timer RESET (previous cancelled, new 300ms)
User types "l"     → Timer RESET again
User types "l"     → Timer RESET again
User types "o"     → Timer RESET again
                   → 300ms passes with no new keystrokes
                   → fn("hello") fires ONCE

Without debounce: 5 API calls ("h", "he", "hel", "hell", "hello")
With debounce:    1 API call ("hello")

The key insight is clearTimeout. Every new call cancels the previous timer and starts a fresh one. The function only executes when the caller stops calling for the full delay period. This is fundamentally different from throttle, which fires at regular intervals regardless.

Why This Matters

impact.txt
Search box without debounce:
  User types 10 characters → 10 API requests → server load, race conditions

Search box with 300ms debounce:
  User types 10 characters → 1 API request → clean, predictable behavior

Window resize without debounce:
  Dragging edge for 2 seconds → 100+ layout recalculations → UI jank

Window resize with 150ms debounce:
  Dragging edge for 2 seconds → 1 layout recalculation → smooth UI

Usage Example

usage.js
// Search input — wait until user stops typing
const searchInput = document.getElementById('search');
const handleSearch = debounce((e) => {
    fetch(`/api/search?q=${e.target.value}`)
        .then(res => res.json())
        .then(renderResults);
}, 300);
searchInput.addEventListener('input', handleSearch);

// Window resize — recalculate layout once
const handleResize = debounce(() => {
    recalculateLayout();
}, 150);
window.addEventListener('resize', handleResize);

// Form auto-save — save after user stops editing
const handleChange = debounce(() => {
    saveDraft(getFormData());
}, 1000);
form.addEventListener('input', handleChange);

Use debounce when you want to wait for the user to finish an action. For search inputs, 200 to 400 milliseconds is the sweet spot. For resize and scroll handlers, 100 to 200 milliseconds works well. If you need the function to fire at regular intervals while the action continues, use throttle instead.