Listening to
JavaScript
Performance
Web
Engineering
Debounce and Throttle
Exploring JavaScript's Frequent Event Handlers.
TRACTION
iamtraction
3 min readDec 28, 2019

Modern web applications often respond to events that can fire rapidly, such as scrolling, resizing, or typing. If not managed carefully, these high-frequency events can degrade performance and user experience. Two essential techniques for controlling event handler execution are debounce and throttle. Both are used to limit how often a function runs, but they serve different purposes and behave differently.

Frequent Event Handlers

Debounce

Debouncing ensures a function is only executed after a certain period has passed since the last event. This is ideal when you want to wait for a pause in activity before running your function, such as waiting until a user has finished typing before validating input or making an API call.

How it Works

A timer is set each time the function is invoked. If it's called again before the timer completes, the timer is reset. Only after the user stops triggering the event for the given time does the callback execute.

This behaviour is for trailing edge debouncing. You can also configure it to execute immediately on the first event (leading edge) and ignore subsequent calls until the delay passes. It does not execute again after the user stops interacting, unless configured for both leading and trailing execution.

Example

// trailing edge debounce
function debounce(callback, wait = 300) {
    let timeout;
    return function (...args) {
        const context = this;
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            callback.apply(context, args);
        }, wait);
    };
}

Use Case

Debounce is commonly used for:

  • Search input fields - Only send an API request after the user stops typing.
  • Window resizing - Only recalculate layout after resizing ends.
  • Auto-save or batch updates - Reduce server calls by waiting for a pause in user actions.

Throttle

Throttling ensures a function is executed at most once every specified interval, regardless of how many times the event is triggered. This is useful for events that happen in quick succession, where you want regular, controlled updates, such as during scrolling or dragging where you want consistent updates without overwhelming the browser.

How it Works

A throttled function executes immediately, then ignores subsequent calls until the interval passes.

This behaviour is for leading edge throttling. You can also configure it to execute at the end of the interval (trailing edge), which delays the callback until the throttling period finishes, ensuring the last event is handled.

Example

// leading edge throttle
function throttle(callback, wait = 300) {
    let lastTime = 0;
    return function (...args) {
        const now = Date.now();
        if (now - lastTime >= wait) {
            lastTime = now;
            callback.apply(this, args);
        }
    };
}

Use Case

Throttle is typically used for:

  • Scroll events - Run animations or UI updates in sync with scroll.
  • Resize events - Avoid too frequent calculations.
  • Limiting how often the handler executes during continuous actions like dragging or mouse movement.

RequestAnimationFrame (rAF) - Browser-Optimized Throttling

window.requestAnimationFrame is a browser API that schedules a function to run before the next repaint, typically syncing with the display's refresh rate (typically every 16ms at 60Hz) but can vary depending on device and browser performance. While not a throttle in the strict sense, it limits execution to once per animation frame, making it ideal for visual updates during rapid events like scrolling or resizing. Unlike throttle, rAF does not guarantee a fixed interval, but aligns updates with the browser's rendering cycle for smoother animations.

Example:

let latestValue;
let rafId;

function updateUI(value) {
    latestValue = value;
    if (rafId) cancelAnimationFrame(rafId);
    rafId = requestAnimationFrame(() => {
        document.querySelector(".element").textContent = latestValue;
    });
}

Use rAF for smooth rendering and visually synced updates rather than for strict control over the execution rate of non-visual logic.

Further Reading

...
Logo
© 2025 - TRACTION