by Mike Guoynes
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.
Order Pad:
empty
Click buttons to add items
Idle
(Idle)
A waiter taking a large group's order doesn't run to the kitchen after every item. Instead, they wait for a pause.
Debounce works the same way: it waits for events to stop before executing the function.
Debounce prevents sending a search request for every keystroke, waiting until the user pauses typing.
debounce waits for a pause before executing. Every event resets the timer; the function only runs when the timer completes.
The pattern:
Perfect for search boxes, autosave, or any scenario where you want to wait until the user is done.
Type "san francisco" slowly and watch how the API request waits until you pause.
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.
Events are processed at fixed intervals.
Unlike debounce, throttle executes at most once every x milliseconds, guaranteeing a regular rhythm regardless of how many events fire.
People Waiting
Click button to join line
Station Idle
Happy Riders!
A rollercoaster departs every 3 seconds on schedule, regardless of how many people join the line.
Throttle works the same way: it executes at a fixed maximum rate, regardless of how many events occur.
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:
Perfect for scroll handlers, resize events, or anywhere you need to limit execution frequency while events are happening.
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.
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:
fn): The task you want to perform (e.g., update scroll position).delay): The minimum time that must pass between executions. Like the turnstile's fixed rotation time.throttleWrapper):fn immediately & closes the gate for the delay.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.
Move your mouse across the pad to see debounce and throttle in action.
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.
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.