Debounce vs. throttle

by Mike Guoynes

Debounce vs Throttle visualization graphic

Intro

Typing in a search box triggers many events. Handling every one leads to sluggish interfaces and too many API requests.

Debounce and throttle control how frequently event handlers run, optimizing performance.

Debounce: the food ordering analogy

Interactive
πŸ‘¨β€πŸ³Waiter

Order Pad:

empty

Click buttons to add items

Idle

🍽️Kitchen

(Idle)

A waiter taking a large group's order doesn't run to the kitchen after every item. Instead, they wait for a pause.

  • Customer adds item β†’ Timer resets
  • Customer adds another β†’ Timer resets again
  • Customer pauses β†’ Waiter submits the complete order

Debounce works the same way: it waits for events to stop before executing the function.

Debounce use case: autocomplete search

Demo
API

Debounce prevents sending a search request for every keystroke, waiting until the user pauses typing.

What is debounce?

debounce waits for a pause before executing. Every event resets the timer; the function only runs when the timer completes.

The pattern:

  • Event fires β†’ Start timer
  • Another event fires β†’ Reset timer
  • Pause (timer completes) β†’ Run the function

Perfect for search boxes, autosave, or any scenario where you want to wait until the user is done.

Debounce code

Try it out

Type "san francisco" slowly and watch how the API request waits until you pause.

debounce.js
1function debounce(fn, gapTime) {
2 let timer;
3
4 return (...args) => {
5 clearTimeout(timer);
6 timer = setTimeout(() => {
7 fn(...args);
8 }, gapTime);
9 };
10}

debounce is a higher-order function: it takes a function and returns a new one.

1. Timer variable

A timer variable persists between calls thanks to closures.

When called, it clears any existing timer with clearTimeout(timer), then sets a new setTimeout. The original function only runs if the timer completes (gapTime milliseconds) without being reset.

Add debounce to your toolchest. Use it for search boxes, autosave, or anywhere you need to wait for a pause.

Throttle

Events are processed at fixed intervals.

Throttle visualization showing function calls at fixed intervals

Unlike debounce, throttle executes at most once every x milliseconds, guaranteeing a regular rhythm regardless of how many events fire.

Throttle: the rollercoaster line analogy

Interactive
πŸšΆβ€β™‚οΈπŸšΆβ€β™€οΈWaiting Line
0

People Waiting

🎒

Click button to join line

Station Idle

πŸŽ‰# Riders Served!
0

Happy Riders!

A rollercoaster departs every 3 seconds on schedule, regardless of how many people join the line.

  • People join β†’ Timer starts
  • More people join β†’ Timer continues
  • After 3 seconds β†’ Ride departs, timer restarts

Throttle works the same way: it executes at a fixed maximum rate, regardless of how many events occur.

What is throttle?

throttle executes at most once every x milliseconds. On each event, it checks if enough time has passed since the last execution. If yes, run immediately; if no, wait until the interval passes.

The pattern:

  • Event fires β†’ Check if interval has passed
  • If yes β†’ Run immediately, start new interval
  • If no β†’ Ignore until next interval

Perfect for scroll handlers, resize events, or anywhere you need to limit execution frequency while events are happening.

Throttle code

Try it out! Scroll quickly within the box below and observe how the position update is throttled.

Add throttle to your toolkit. Use it for scroll handlers, resizing, or any event that fires rapidly. Control the flow.

throttle.js
1const throttle = (fn, delay) => {
2 let isPending = false;
3 let pendingArguments;
4
5 return function throttleWrapper(...args) {
6 const isReady = !isPending;
7
8 if (isReady) {
9 fn(...args); // Call when ready
10 pendingArguments = undefined;
11 isPending = true;
12
13 // Create a new timer
14 setTimeout(() => {
15 isPending = false; // After timeout -> ready.
16 if (pendingArguments) {
17 throttleWrapper(...pendingArguments);
18 }
19 }, delay);
20 } else {
21 pendingArguments = args; // Store the latest arguments.
22 }
23 };
24};

Think of throttle like a turnstile at a subway station. It only lets one person through every few seconds, no matter how many people are lined up.

Key Difference from Debounce:

Unlike debounce (which resets its timer with every new event), throttle guarantees execution at regular intervals. It doesn't care if events keep happening; it just lets one "through the gate" every delay milliseconds.

How it Works - The Essentials:

  • The Function (fn): The task you want to perform (e.g., update scroll position).
  • The Interval (delay): The minimum time that must pass between executions. Like the turnstile's fixed rotation time.
  • The Gatekeeper (throttleWrapper):
    • When called, it checks if the "gate" is open (enough time has passed).
    • If open: Runs your fn immediately & closes the gate for the delay.
    • If closed: It might remember the *latest* call attempt. When the gate reopens, it could execute using those remembered arguments.

Use Cases:

Perfect for rate-limiting events that fire continuously, like scroll handlers, window resize events, or dragging operations.

Try it, write this code on paper. Then code it. Try to break it. Reverse engineer it.

Add throttle to your toolkit. Use it for scroll handlers, resizing, or any event that fires rapidly. Control the flow. Leverage it. Own it. It's yours now.

Interactive Visualizer

Move your mouse across the pad to see debounce and throttle in action.

Events
Debounce (500ms)
Throttle (300ms)
πŸ‘†Move Mouse / Touch Here

How They Work

Debounce

Waits for a pause (500ms) in events. Triggers only once after the last event in a rapid series stops for the specified duration. Good for actions like auto-saving or search suggestions after typing stops.

Throttle

Limits execution to once every interval (300ms), regardless of how many events fire. Ensures a function runs regularly but not excessively. Useful for rate-limiting scroll or resize handlers.