ReviseAlgo Logo

Performance & Optimization

Debounce & Throttle

Master rate-limiting in JavaScript. Learn to implement and configure Debounce and Throttle wrappers to optimize event handler performance.

Last Updated: July 15, 2026 10 min read

1. Introduction

Debouncing and Throttling are rate-limiting techniques used to control how often a function is executed. They prevent performance bottlenecks by limiting the execution frequency of high-frequency events (like scroll, resize, or keyup events).

2. Why It Matters

Some browser events (like scrolling or resizing the window) can fire dozens of times per second. If an event handler performs expensive operations (like DOM manipulation or API requests) directly, it will overload the browser's main thread, causing lag and frame drops. Rate-limiting these events keeps the UI responsive.

3. Real-World Analogy

Think of a Lift Elevator door vs a Train Station turnstile:

  • Debounce (Lift Elevator Door): The elevator door stays open when passengers walk in. If a new passenger walks in, the timer resets and the door stays open. The door only closes (executes action) when there is a pause in passengers walking in (silence delay). This is useful for search auto-completes (wait until the user stops typing).
  • Throttle (Train Station Turnstile): A turnstile opens once every 2 seconds, regardless of how many people stand in line or push against it. It limits execution to a steady, constant interval. This is useful for scroll handlers or rate-limiting button clicks.

4. Debouncing

Debouncing delays execution until a specified delay has passed since the last time the function was called. If the function is called again before the delay expires, the timer resets:

5. Throttling

Throttling limits function execution to a maximum of once per specified time interval, ignoring subsequent calls during that interval:

6. Practical Example

This script demonstrates using debounce to optimize an autocomplete search field, triggering search requests only when the user stops typing for 300ms:

7. Common Mistakes

  • Re-creating debounced/throttled functions inside render loops: In reactive frameworks (like React), instantiating a debounced function directly inside a functional component body re-creates the function on every render, resetting its timers. Define debounced functions outside the component scope or wrap them in hooks like useMemo or useCallback.

8. Quick Quiz

Q1: Which rate-limiting technique should you use to delay an API search request until a user stops typing?

A) Throttle

B) Debounce

Answer: B — Debouncing waits until there is a pause in inputs before executing the function, making it ideal for search inputs.

9. Scenario-Based Challenge

The Window Resize Auto-Layout Adaptor:

A dashboard contains charts that must recalculate layout dimensions when the browser window is resized. Since window resize events fire frequently, wrap the layout engine call in a throttle helper to limit recalculations to once every 250ms.

10. Debugging Exercise

Explain why this React component fails to debounce input calls, and how to fix it:

import React, { useState } from 'react';

function SearchBox() { const [val, setVal] = useState('');

// Bug: debounced function is re-created on every render! const queryApi = debounce((q) => fetch(`/api?q=${q}`), 500);

const onChange = (e) => { setVal(e.target.value); queryApi(e.target.value); // Timer resets on every render! };

return <input value={val} onChange={onChange} />; }

View Solution

Diagnosis: On every state update (triggered by setVal), the component re-renders and re-creates the queryApi function. Because the debounced function's closure state is reinitialized on every render, the timer is never preserved, rendering the debounce useless.

Fix: Wrap the debounced function in React's useCallback hook to preserve the function instance across renders:

import React, { useState, useCallback } from 'react';

function SearchBox() { const [val, setVal] = useState('');

// Wrap in useCallback to preserve reference const queryApi = useCallback( debounce((q) => fetch(`/api?q=${q}`), 500), [] );

const onChange = (e) => { setVal(e.target.value); queryApi(e.target.value); };

return <input value={val} onChange={onChange} />; }

11. Interview Questions

🟢 Q1: Compare Debouncing and Throttling and explain their use cases.

Answer:
Debouncing: Delays execution until a specified delay has passed since the last call. If the function is called again before the delay expires, the timer resets.
Use Case: Autocomplete search inputs, auto-save forms.
Throttling: Limits execution to a maximum of once per specified time interval, ignoring subsequent calls during that interval.
Use Case: Scroll and scroll-to-infinite feed listeners, resize events, drag-and-drop trackers.

12. Production Considerations

  • Use Established Implementations: Writing custom rate-limiting wrappers can introduce subtle bugs (like failing to handle final trailing executions or losing the this context). In production environments, use utility libraries (like Lodash or throttle-debounce) that provide robust implementations.