The following is a guest post by David Corbacho, a front end engineer in London. We’ve broached this topic before, but this time, David is going to drive the concepts home through interactive demos that make things very clear.
Debounce and throttle are two similar (but different!) techniques to control how many times we allow a function to be executed over time.
Having a debounced or throttled version of our function is especially useful when we are attaching the function to a DOM event. Why? Because we are giving ourselves a layer of control between the event and the execution of the function. Remember, we don’t control how often those DOM events are going to be emitted. It can vary.
For example, let’s talk about scroll events. See this example:
See the Pen Scroll events counter by Corbacho (@dcorb) on CodePen.
When scrolling using a trackpad, scroll wheel, or just by dragging a scrollbar can trigger easily 30 events per second. But scrolling slowly (swapping) in a smartphone could trigger as much as 100 events per second during my tests. Is your scroll handler prepared for this rate of execution?
In 2011, an issue popped up on the Twitter website: when you were scrolling down your Twitter feed, it became slow and unresponsive. John Resig published a blog post about the problem where it was explained how bad of an idea it is to directly attach expensive functions to the scroll
event.
The suggested solution by John (at that time, five years ago) was a loop running every 250ms, outside of the onScroll event
. That way the handler is not coupled to the event. With this simple technique, we can avoid ruining the user experience.
These days there are slightly more sophisticated ways of handling events. Let me introduce you to Debounce, Throttle, and requestAnimationFrame. We’ll also look at the matching use cases.
Debounce
The Debounce technique allow us to “group” multiple sequential calls in a single one.

Imagine you are in an elevator. The doors begin to close, and suddenly another person tries to get on. The elevator doesn’t begin its function to change floors, the doors open again. Now it happens again with another person. The elevator is delaying its function (moving floors), but optimizing its resources.
Try it for yourself. Click or move the mouse on top of the button:
See the Pen Debounce. Trailing by Corbacho (@dcorb) on CodePen.
You can see how sequential fast events are represented by a single debounced event. But if the events are triggered with big gaps, the debouncing doesn’t happen.
Leading edge (or “immediate”)
You may find it irritating that the debouncing event waits before triggering the function execution, until the events stop happening so rapidly. Why not trigger the function execution immediately, so it behaves exactly as the original non-debounced handler? But not fire again until there is a pause in the rapid calls.
You can do this! Here’s an example with the leading
flag on:

In underscore.js, the option is called immediate
instead of leading
Try it for yourself:
See the Pen Debounce. Leading by Corbacho (@dcorb) on CodePen.
Debounce Implementations
The first time I saw debounce implemented in JavaScript was in 2009 in this John Hann post (who also coined the term).
Soon after that, Ben Alman created a jQuery plugin (no longer maintained), and a year after, Jeremy Ashkenas added it to underscore.js. It was later added to Lodash, a drop-in alternative to underscore.
The 3 implementations are a bit different internally, but their interface is almost identical.
There was a time that underscore adopted the debounce/throttle implementation from Lodash, after I discovered a bug in the _.debounce
function in 2013. Since then, both implementations have grown apart.
Lodash has added more features to its _.debounce
and _.throttle
functions. The original immediate
flag was replaced with leading
and trailing
options. You can choose one, or both. By default, only the trailing
edge is enabled.
The new maxWait
option (only in Lodash at the moment) is not covered in this article but it can be very useful. Actually, the throttle function is defined using _.debounce
with maxWait
, as you see in the lodash source code.
Debounce Examples
Resize Example
When resizing a (desktop) browser window, they can emit many resize
events while dragging the resize handle.
See for yourself in this demo:
See the Pen Debounce Resize Event Example by Corbacho (@dcorb) on CodePen.
As you can see, we are using the default trailing
option for the resize event, because we are only interested on the final value, after user stops resizing the browser.
keypress on autocomplete form with Ajax request
Why to send Ajax requests to the server every 50ms, when the user is still typing? _.debounce
can help us, avoiding extra work, and only send the request when the user stops typing.
Here, it wouldn’t make sense to have the leading
flag on. We want to wait to the last letter typed.
See the Pen Debouncing keystrokes Example by Corbacho (@dcorb) on CodePen.
A similar use case would be to wait until user stops typing before validate its input. “Your password is too short” type of messages.
How to use debounce and throttle and common pitfalls
It can be tempting to build your own debounce/throttle function, or copy it from some random blog post. My recommendation is to use underscore or Lodash directly. If you only need the _.debounce
and _.throttle
functions, you can use Lodash custom builder to output a custom 2KB minified library. Build it with this simple command:
npm i -g lodash-cli
lodash include = debounce, throttle
That said, most use the modular form `lodash/throttle` and `lodash/debounce` or `lodash.throttle` and `lodash.debounce` packages with webpack/browserify/rollup.
A common pitfall is to call the _.debounce
function more than once:
// WRONG
$(window).on('scroll', function() {
_.debounce(doSomething, 300);
});
// RIGHT
$(window).on('scroll', _.debounce(doSomething, 200));
Creating a variable for the debounced function will allow us to call the private method debounced_version.cancel()
, available in lodash and underscore.js, in case you need it.
var debounced_version = _.debounce(doSomething, 200);
$(window).on('scroll', debounced_version);
// If you need it
debounced_version.cancel();
Throttle
By using _.throttle
, we don’t allow to our function to execute more than once every X milliseconds.
The main difference between this and debouncing is that throttle guarantees the execution of the function regularly, at least every X milliseconds.
The same way than debounce, throttle technique is covered by Ben’s plugin, underscore.js and lodash.
Throttling Examples
Infinite scrolling
A quite common example. The user is scrolling down your infinite-scrolling page. You need to check how far from the bottom the user is. If the user is near the bottom, we should request via Ajax more content and append it to the page.
Here our beloved _.debounce
wouldn’t be helpful. It only would trigger only when the user stops scrolling.. and we need to start fetching the content before the user reaches the bottom.
With _.throttle
we can warranty that we are checking constantly how far we are from the bottom.
See the Pen Infinite scrolling throttled by Corbacho (@dcorb) on CodePen.
requestAnimationFrame (rAF)
requestAnimationFrame
is another way of rate-limiting the execution of a function.
It can be thought as a _.throttle(dosomething, 16)
. But with a much higher fidelity, since it’s a browser native API that aims for better accuracy.
We can use the rAF API, as an alternative to the throttle function, considering this pros/cons:
Pros
- Aims for 60fps (frames of 16 ms) but internally will decide the best timing on how to schedule the rendering.
- Fairly simple and standard API, not changing in the future. Less maintenance.
Cons
- The start/cancelation of rAFs it’s our responsibility, unlike
.debounce
or.throttle
, where it’s managed internally. - If the browser tab is not active, it would not execute. Although for scroll, mouse or keyboard events this doesn’t matter.
- Although all modern browsers offer rAF, still is not supported in IE9, Opera Mini and old Android. A polyfill would be needed still today.
- rAF is not supported in node.js, so you can’t use it on the server to throttle filesystem events.
As a rule of thumb, I would use requestAnimationFrame
if your JavaScript function is “painting” or animating directly properties, use it at everything that involves re-calculating element positions.
To make Ajax requests, or deciding if adding/removing a class (that could trigger a CSS animation), I would consider _.debounce
or _.throttle
, where you can set up lower executing rates (200ms for example, instead of 16ms)
If you think that rAF could be implemented inside underscore or lodash, they both have rejected the idea, since it’s a specialized use case, and it’s easy enough to be called directly.
Examples of rAF
I will cover only this example to use requestAnimation frame on scroll, inspired by Paul Lewis article, where he explains step-by-step the logic of this example.
I put it side by side to compare it to _.throttle
at 16ms. Giving similar performance, but probably rAF will give you better results on more complex scenarios.
See the Pen Scroll comparison requestAnimationFrame vs throttle by Corbacho (@dcorb) on CodePen.
A more advanced example where I’ve seen this technique is in the library headroom.js, where the logic is decoupled and wrapped inside an object.
Conclusion
Use debounce, throttle and requestAnimationFrame
to optimize your event handlers. Each technique is slightly different, but all three of them are useful and complement each other.
In summary:
- debounce: Grouping a sudden burst of events (like keystrokes) into a single one.
- throttle: Guaranteeing a constant flow of executions every X milliseconds. Like checking every 200ms your scroll position to trigger a CSS animation.
- requestAnimationFrame: a throttle alternative. When your function recalculates and renders elements on screen and you want to guarantee smooth changes or animations. Note: no IE9 support.
Thank you for a very detailed article. This actually solves a problem I am having at this very moment. ^_^
Wow, excellent stuff! I was always confused with the differences between debouncing, throttling and rAF 3 and this post makes it very clear for me!
Quick question about the lodash cli, once installed globally:
Does this create a lodash folder in your
node_modules
that only contains these 2 included packages?It creates a
lodash.custom.js
&lodash.custom.min.js
in the current working directory.Also, to use lodash-cli you just call lodash.
So it should actually be
lodash include=debounce, throttle
That should be without the space, sorry.
Great breakdown of
throttle
anddebounce
! The visuals are quite helpful.For throttling mostly visual tasks like animations & scroll triggers, I tend to use
requestAnimationFrame
like in the last example, but I simplify by using the request ID, the positive long integer thatrequestAnimationFrame
returns, as theticking
value.If the scroll handler is called multiple times, a shorthand check on
ticking
will ensurerequestAnimationFrame
doesn’t get called again until reset in theupdate
function.http://codepen.io/shshaw/pen/PNEwZd?editors=0010
Thanks for the explanation. For the people who don’t want to install an entire library just to debounce / throttle, how does David Walsh’s function fit into all of this?
https://davidwalsh.name/javascript-debounce-function
As David showed us in the text, you can choose to extract just the debounce/throttle functions.
But like you maybe, i prefer to use my own solutions: https://gist.github.com/vincentorback/9649034
Hi Donnie, I wrote this article.
I think David Walsh’s function is ok but it’s just a version, frozen on time, on how it used to be implemented in underscore.js years ago. Several improvements have happened since then. For example, the “later” function doesn’t need to be inside of the scope of the return function, and other improvements.
There are some optimizations that are in lodash, based on this blog post, that are not in David Walsh’s version http://modernjavascript.blogspot.co.uk/2013/08/building-better-debounce.html
Vincent snippet seems good at first sight, I like it a lot. Similar to Cruz’s “simpler” debounce:
https://github.com/rstacruz/simpler-debounce/blob/master/index.js
but Vincent version supports passing arguments thanks to that extra closure. Would it work in IE9 and older IE? (Because they don’t support passing multiple arguments for setTimeout). Also not supporting the “leading” option, that is really handy.
Oh, and Both versions David Walsh and Vincent debounce don’t support return the value of the original function, or .cancel methods, .. extra features that you might need at some point.
I also was tempted to post a “debounce” version along with this article. But I don’t like to promote the habit of copy/pasting functions that you don’t understand.
As general advice, I still recommend to use Underscore or lodash directly, as they are optimized/scrutinized by hundreds of developers and continuously improved, I would trust them, unless you know what you are doing.
http://codepen.io/vlrprbttst/pen/jbKLeo a pen about debouncing on window resize!
I really appreciate the article, it’s a great demonstration of ways to rate limit events and your visuals are fantastic! (seriously, they’re great!).
Debounce wasn’t coined by John Hann, though. It literally comes from a similar technique used with hardware switches, where metal contacts would “bounce” together when a button was pressed, triggering repeated signals in the circuitry. Your keyboard has debounce circuits for each key (or often software implementations, nowadays), that do basically the same thing.
Thanks NT, I spent couple of weeks refining the visuals. I’m glad people appreciate them
Great stuff, thank you!
The last example is a little bit misleading. Scroll events like other UI related events (mousemove, resize etc.) are already bound to the frame life cycle and a modern browser (i.e.: browser that support rAF) does never call a scroll event twice inside of one frame. Which means, that
ticking
is nevertrue
at the timerequestTick
is called. It would only make sense, if you would add a second eventhandler ("resize"
for example), that callsrequestTick
.You can simply check this by changing your code to this:
You can check this codepen and open your debug console for the output.
Good point! I didn’t think about that. As I said in the article, the example was inspired on the Paul Lewis article, I’m cross-posting your comment to http://www.html5rocks.com/en/tutorials/speed/animations/#comment-2663181154, I hope Paul has some insight here
I think you need to update your lodash-cli command. I’m completely new to lodash and it took me ages to figure out that I had to use
lodash
instead oflodash-cli
in the command line. So the command that worked for me was: