Summary
In this blog post, Let’s focus on understanding debounce and throttle in javascript and explore these two fundamental performance-optimization techniques in JavaScript: debouncing and throttling. You’ll learn what each pattern does, how they differ, and when to use one over the other. We’ll examine both custom implementations and Lodash-based examples, complete with clear, annotated code snippets. By the end, you’ll have practical guidelines and reference functions you can drop into your projects to prevent expensive operations—like scroll handlers or autocomplete requests—from firing too often, ensuring a smoother user experience.
Introduction
Modern web applications often attach handlers to events that can fire dozens or hundreds of times per second—think scroll, resize, or input events. Without control, these handlers can degrade performance and overwhelm your application or backend services.
Debounce and throttle are two related—but distinct—techniques that help you limit how often your functions execute, improving responsiveness and reducing unnecessary work.
What Is Debounce?
Debouncing ensures that a function is invoked only after a specified interval has elapsed since the last time it was called. In other words, the function “waits” until the user pauses their input or stops the event storm.
This is ideal for scenarios like search-input suggestions or auto-saving text fields, where you only want to fire once the user has stopped typing.
Key Characteristics
- Combines multiple rapid calls into one.
- Resets the timer on each invocation.
- Only executes after the “quiet” period ends.

What Is Throttle?
Throttling ensures that a function is invoked at most once in a specified time window, no matter how many times the event fires. It “paces” the calls, allowing periodic execution.
This pattern is ideal for scroll or resize events where you want regular updates rather than a single delayed action.
Key Characteristics
- Guarantees execution at regular intervals.
- Ignores calls that happen within the wait period.
- Can be configured to fire on the leading and/or trailing edge of the interval.
Debounce vs. Throttle: Side-by-Side Comparison
| Aspect | Debounce | Throttle |
| Invocation pattern | Fires once after events stop | Fires at most once every interval |
| Use case examples | Autocomplete search, form validation, window resize events | Scroll position updates, analytics pings, navigation throttles |
| Under heavy load | Merges burst into single call | Maintains steady rate of calls |
| Leading/trailing ops | Usually trailing only (with options for leading) | Configurable for leading and trailing edges |
- Debounce waits until events stop (e.g., typing).
- Throttle fires periodically during an event storm (e.g., scrolling).
Custom Implementations of Debounce and Throttle in JavaScript
Debounce Function
function debounce(func, wait = 300) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
Explanation:

- We store a timeoutId in closure.
- Each call clears the previous timer (if any).
- We set a new timer to invoke func after wait
Throttle Function
function throttle(func, interval = 300) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
func.apply(this, args);
}
};
}
Explanation:
- We track the timestamp of the last invocation.
- On each call, we check if enough time (interval) has passed.
- If yes, we update lastTime and invoke func.
Using Lodash Utilities
Lodash provides battle-tested implementations with additional features:
- debounce(func, wait, [options]): built-in cancel and flush methods; choose leading/trailing behavior ([lodash.info][3]).
- throttle(func, wait, [options]): built‑in cancel and flush; configure leading/trailing edges ([lodash.info][5]).
import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle';
const optimizedDebounce = debounce(apiCall, 500, { leading: false, trailing: true });
const optimizedThrottle = throttle(onScroll, 200, { leading: true, trailing: false });
Key Takeaways: Both _.debounce and _.throttle return enhanced functions that include built‑in methods—namely .cancel() to abort pending calls and .flush() to immediately invoke them—and accept an options object to control leading (invoke at start) and trailing (invoke at end) behavior. Understanding these options lets you fine‑tune exactly when your handler fires during bursts of events, preventing both premature and redundant calls.
Lodash’s Built‑In Methods
.cancel()
Both debounced and throttled functions expose a .cancel() method. Calling it discards any pending invocation that was scheduled but not yet fired.
- Use case (debounce): Aborting a delayed save operation when a form is reset.
- Use case (throttle): Stopping a periodic analytics ping when the user navigates away.
.flush()
Likewise, both come with .flush(), which immediately invokes the original function if a call is pending, then clears the timer.
- Use case (debounce): Forcing an immediate search request when the user submits a form.
- Use case (throttle): Ensuring the final scroll position update happens before teardown.
Leading vs. Trailing Options
Both utilities accept an options object:
_.debounce(func, wait, { leading, trailing, maxWait })
_.throttle(func, wait, { leading, trailing })
- leading: true – Invoke func immediately on the first call within the window.
- trailing: true – Invoke func after the window finishes, using the last arguments seen.
- maxWait (debounce only) – Ensures that, even during constant calls, func is invoked at least every maxWait
By default:
- Debounce: { leading: false, trailing: true }
- Throttle: { leading: true, trailing: true }
Debounce Behaviors
- Default ({leading:false, trailing:true}): Fires only once, after calls have stopped for wait
- Leading only ({leading:true, trailing:false}): Fires immediately on first call, then ignores subsequent calls until wait
- Both edges ({leading:true, trailing:true}): Fires once on first call, then again after calls stop for wait ms—unless only a single call occurred, in which case it fires only on the leading edge.
const search = _.debounce(doSearch, 500, {
leading: true,
trailing: true,
maxWait: 2000
});
Throttle Behaviors
- Default ({leading:true, trailing:true}): Fires immediately, then ensures at most one call per wait ms, firing again after the window if calls occurred during it.
- Leading only ({leading:true, trailing:false}): Fires immediately, then suppresses any trailing invocation.
- Trailing only ({leading:false, trailing:true}): Suppresses the initial call, then fires once at the end of each window if calls occurred.
const onScroll = _.throttle(updateScroll, 100, {
leading: false,
trailing: true
});

Putting It All Together
| Method | Built‑In Methods | Leading Default | Trailing Default | Extra Option |
| _.debounce | .cancel(), .flush() | false | true | maxWait |
| _.throttle | .cancel(), .flush() | true | true | — |
- When to choose leading edges: Provide instant feedback (e.g., button disabling, immediate analytics ping).
- When to choose trailing edges: Batch up final results (e.g., final input value search, end-of-scroll position).
Practical Use Cases for Understanding Debounce and Throttle in JavaScript
1. Search Autocomplete (Debounce)
Throttle would trigger too early, sending many requests as the user types. Debounce waits until the user pauses, reducing network traffic.
2. Infinite Scroll (Throttle)
You want to check scroll position at most once every 200ms, ensuring the check runs reliably without overwhelming the browse.
3. Window Resize (Either)
Use debounce to update layout once resizing stops; use throttle to track progress during resizing for live feedback.
Conclusion
Understanding between debounce and throttle will allow us to Choose between debounce and throttle hinges on your UI requirements:
- Debounce for “after the fact” actions—search inputs, form validations, auto-saves.
- Throttle for “in-flight” control—scroll handlers, live analytics, interactive visualizations.
With the custom functions and Lodash utilities above, you can easily integrate either pattern into your codebase, ensuring your application remains performant, responsive, and resource-efficient.
Do feel free to Contact Us or Schedule a Call to discuss any of your projects
Author
Yugma N. Gandhi
Our experienced full stack developer excels in the MERN stack. Soft-spoken, smart, and agile, he embodies a core developer spirit and never loses hope until the end. Learning and adaptability are his strengths. He is skilled in performance tuning, migration, and data visualization, summarizing complex data with a bird's eye view on dashboards. Providing simple solutions to complex problems is his preferred execution style.