{"id":240286,"date":"2016-04-06T12:24:38","date_gmt":"2016-04-06T19:24:38","guid":{"rendered":"http:\/\/css-tricks.com\/?p=240286"},"modified":"2018-01-03T14:07:56","modified_gmt":"2018-01-03T21:07:56","slug":"debouncing-throttling-explained-examples","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/debouncing-throttling-explained-examples\/","title":{"rendered":"Debouncing and Throttling Explained Through Examples"},"content":{"rendered":"
The following is a guest post by David Corbacho<\/a>, a front end engineer in London. We’ve broached this topic before<\/a>, but this time, David is going to drive the concepts home through interactive demos that make things very clear.<\/em><\/p>\n <\/p>\n Debounce<\/strong> and throttle<\/strong> are two similar (but different!) techniques to control how many times we allow a function to be executed over time.<\/p>\n 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.<\/p>\n For example, let’s talk about scroll events. See this example:<\/p>\n See the Pen Scroll events counter<\/a> by Corbacho (@dcorb<\/a>) on CodePen<\/a>.<\/p>\n 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?<\/p>\n 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<\/a> where it was explained how bad of an idea it is to directly attach expensive functions to the The suggested solution by John (at that time, five years ago) was a loop running every 250ms, outside of the 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.<\/p>\n The Debounce technique allow us to “group” multiple sequential calls in a single one. <\/p>\n 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.<\/p>\n Try it for yourself. Click or move the mouse on top of the button:<\/p>\n See the Pen Debounce. Trailing<\/a> by Corbacho (@dcorb<\/a>) on CodePen<\/a>.<\/p>\n 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. <\/p>\n You may find it irritating that the debouncing event waits<\/em> 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.<\/p>\n You can do this! Here’s an example with the In underscore.js, the option is called Try it for yourself:<\/p>\n See the Pen Debounce. Leading<\/a> by Corbacho (@dcorb<\/a>) on CodePen<\/a>.<\/p>\n The first time I saw debounce implemented in JavaScript was in 2009 in this John Hann post<\/a> (who also coined the term).<\/p>\n Soon after that, Ben Alman created a jQuery plugin<\/a> (no longer maintained), and a year after, Jeremy Ashkenas added it to underscore.js<\/a>. It was later added to Lodash, a drop-in alternative to underscore.<\/p>\n The 3 implementations are a bit different internally, but their interface is almost identical.<\/p>\n There was a time that underscore adopted the debounce\/throttle implementation from Lodash, after I discovered a bug<\/a> in the Lodash has added<\/a> more features to its The new When resizing a (desktop) browser window, they can emit many See for yourself in this demo:<\/p>\n See the Pen Debounce Resize Event Example<\/a> by Corbacho (@dcorb<\/a>) on CodePen<\/a>.<\/p>\n As you can see, we are using the default Why to send Ajax requests to the server every 50ms, when the user is still typing? Here, it wouldn’t make sense to have the See the Pen Debouncing keystrokes Example<\/a> by Corbacho (@dcorb<\/a>) on CodePen<\/a>.<\/p>\n A similar use case would be to wait until user stops typing before validate its input. “Your password is too short” type of messages.<\/p>\n 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.<\/strong> If you only need the That said, most use the modular form `lodash\/throttle` and `lodash\/debounce` or `lodash.throttle` and `lodash.debounce` packages with webpack\/browserify\/rollup.<\/p>\n A common pitfall is to call the Creating a variable for the debounced function will allow us to call the private method By using The main difference between this and debouncing is that throttle guarantees the execution of the function regularly, at least every X milliseconds.<\/p>\n The same way than debounce, throttle technique is covered by Ben’s plugin, underscore.js and lodash.<\/p>\n 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.<\/p>\n Here our beloved See the Pen Infinite scrolling throttled<\/a> by Corbacho (@dcorb<\/a>) on CodePen<\/a>.<\/p>\n It can be thought as a We can use the rAF API, as an alternative to the throttle function, considering this pros\/cons:<\/p>\n As a rule of thumb, I would use To make Ajax requests, or deciding if adding\/removing a class (that could trigger a CSS animation), I would consider 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.<\/p>\n I will cover only this example to use requestAnimation frame on scroll, inspired by Paul Lewis article<\/a>, where he explains step-by-step the logic of this example.<\/p>\n I put it side by side to compare it to See the Pen Scroll comparison requestAnimationFrame vs throttle<\/a> by Corbacho (@dcorb<\/a>) on CodePen<\/a>.<\/p>\nscroll<\/code> event.<\/p>\n
onScroll event<\/code>. That way the handler is not coupled to the event. With this simple technique, we can avoid ruining the user experience. <\/p>\n
Debounce<\/h3>\n
<\/figure>\n
Leading edge (or “immediate”)<\/h4>\n
leading<\/code> flag on:<\/p>\n
immediate<\/code> instead of
leading<\/code><\/p>\n
Debounce Implementations<\/h4>\n
_.debounce<\/code> function in 2013. Since then, both implementations have grown apart. <\/p>\n
_.debounce<\/code> and
_.throttle<\/code> functions. The original
immediate<\/code> flag was replaced with
leading<\/code> and
trailing<\/code> options. You can choose one, or both. By default, only the
trailing<\/code> edge is enabled.<\/p>\n
maxWait<\/code> 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<\/code> with
maxWait<\/code>, as you see in the lodash source code<\/a>.<\/p>\n
Debounce Examples<\/h4>\n
Resize Example<\/h5>\n
resize<\/code> events while dragging the resize handle.<\/p>\n
trailing<\/code> option for the resize event, because we are only interested on the final value, after user stops resizing the browser.<\/p>\n
keypress on autocomplete form with Ajax request<\/h5>\n
_.debounce<\/code> can help us, avoiding extra work, and only send the request when the user stops typing.<\/p>\n
leading<\/code> flag on. We want to wait to the last letter typed.<\/p>\n
How to use debounce and throttle and common pitfalls<\/h3>\n
_.debounce<\/code> and
_.throttle<\/code> functions, you can use Lodash custom builder to output a custom 2KB minified library. Build it with this simple command:<\/p>\n
npm i -g lodash-cli\r\nlodash include = debounce, throttle<\/code><\/pre>\n
_.debounce<\/code> function more than once:<\/p>\n
\/\/ WRONG\r\n$(window).on('scroll', function() {\r\n _.debounce(doSomething, 300); \r\n});\r\n\r\n\/\/ RIGHT\r\n$(window).on('scroll', _.debounce(doSomething, 200));<\/code><\/pre>\n
debounced_version.cancel()<\/code>, available in lodash and underscore.js, in case you need it.<\/p>\n
var debounced_version = _.debounce(doSomething, 200);\r\n$(window).on('scroll', debounced_version);\r\n\r\n\/\/ If you need it\r\ndebounced_version.cancel();<\/code><\/pre>\n
Throttle<\/h3>\n
_.throttle<\/code>, we don’t allow to our function to execute more than once every X milliseconds. <\/p>\n
Throttling Examples<\/h4>\n
Infinite scrolling<\/h5>\n
_.debounce<\/code> wouldn’t be helpful. It only would trigger only when the user stops scrolling.. and we need to start fetching the content before<\/em> the user reaches the bottom.
\nWith _.throttle<\/code> we can warranty that we are checking constantly how far we are from the bottom.<\/p>\n
requestAnimationFrame (rAF)<\/h3>\n
requestAnimationFrame<\/code> is another way of rate-limiting the execution of a function.<\/p>\n
_.throttle(dosomething, 16)<\/code>. But with a much higher fidelity, since it’s a browser native API that aims for better accuracy.<\/p>\n
Pros<\/h4>\n
\n
Cons<\/h4>\n
\n
.debounce<\/code> or
.throttle<\/code>, where it’s managed internally.<\/li>\n
requestAnimationFrame<\/code> if your JavaScript function is “painting” or animating directly properties, use it at everything that involves re-calculating element positions.<\/p>\n
_.debounce<\/code> or
_.throttle<\/code>, where you can set up lower executing rates (200ms for example, instead of 16ms)<\/p>\n
Examples of rAF<\/h4>\n
_.throttle<\/code> at 16ms. Giving similar performance, but probably rAF will give you better results on more complex scenarios.<\/p>\n