I got these confused the other day and someone corrected me. So I tossed it on the ol’ list of blog post ideas and here we are. Both of them are ways to limit the amount of JavaScript you are executing based on DOM events for performance reasons. But they are, you guessed it, different.
Throttling enforces a maximum number of times a function can be called over time. As in “execute this function at most once every 100 milliseconds.”
Say under normal circumstances you would call this function 1,000 times over 10 seconds. If you throttle it to only once per 100 milliseconds, it would only execute that function at most 100 times
(10s * 1,000) = 10,000ms
10,000ms / 100ms throttling = 100 maximum calls
Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called. As in “execute this function only if 100 milliseconds have passed without it being called.”
Perhaps a function is called 1,000 times in a quick burst, dispersed over 3 seconds, then stops being called. If you have debounced it at 100 milliseconds, the function will only fire once, at 3.1 seconds, once the burst is over. Each time the function is called during the burst it resets the debouncing timer.
What’s the point?
One major use case for these concepts is certain DOM events, like scrolling and resizing. For instance, if you attach a scroll handler to an element, and scroll that element down say 5000px, you’re likely to see 100+ events be fired. If your event handler does a bunch of work (like heavy calculations and other DOM manipulation), you may see performance issues (jank). If you can get away with executing that handler less times, without much interruption in experience, it’s probably worth it.
Quick hit examples:
- Wait until the user stops resizing the window
- Don’t fire an ajax event until the user stops typing
- Measure the scroll position of the page and respond at most every 50ms
- Ensure good performance as you drag elements around in an app
How to do it
Functions for both are built into Underscore and Lodash. Even if you don’t use those libraries wholesale, you could always go extract the functions out of them for your own use.
Simple throttled scroll:
$("body").on('scroll', _.throttle(function() {
// Do expensive things
}, 100));
Simple debounced resize:
$(window).on('resize', _.debounce(function() {
// Do expensive things
}, 100));
Leveling up from here, you would work in the use of requestAnimationFrame, so even when the functions are executed the browser does it on it’s own ideal timing. That’s covered in this Paul Lewis tutorial.
Demo
Simple demo so you can experience the difference:
See the Pen The Difference Between Throttling, Debouncing, and Neither by Chris Coyier (@chriscoyier) on CodePen.
Update: More demos!
I would argue that debouncing based on time is only one of several valid approaches, you can also debounce strictly based on unfinished behaviour.
Consider a button that initiates an add to cart behaviour. You want to ensure that even if a user double clicks accidentally only one item is added to the cart. You can debounce based on time (click more than 500ms apart is 2 clicks, not 1 double click) or you can guard the function with a flag that blocks any further clicks until the flag has been cleared as the process completes.
Please dont assume Ive made mistakes in my intereraction with a common ui element. My car radio debounces so if i try to change the station too quickly i don’t go anywhere and it makes me want to rip the thing out of my dash.
Not a great example as you have access to the double click event already. Just prevent an action on double click.
Apart from what Chris said, using a fixed amount of time to distinguish between a click and a double click is just bad, because the OS might offer the user to configure what time frame constitutes a double click – so your 500ms assumption might simply be wrong.
All valid points, my original thought had been simply, debouncing is not as cut and dry as it’s defined here. Yes, there are probably better examples, and yes one should absolutely be careful where it’s used.
But, an understanding of the debouncing pattern, rather than just time-referenced debouncing is a good tool to have in your pocket.
Any particular reason why this hasn’t been standardized yet?
I thought underscore was pretty standard, I’ve certainly started using it everywhere ;)
Lodash is sort-of the new underscore I’d check that out. It has some extra useful functionality, started out as a fork of underscore.
Easy with the “lodash is the new underscore”
underscore 53k
lodash 407k
lodash is probably more suited to node.js
Throttle: Step, snap, grid. Example: Persistent values on custom range slider.
Debounce: Awaiting for idle. Example: Trigger AJAX search results after typing on a text field, hover state animation trick in dropdown menu → don’t show the dropdown menu except if user stop moving the mouse pointer on the parent menu.
Important note regarding your throttling example: you’ll get a maximum of 100 calls over the 10 seconds in question.
The other 900 calls will be made, though, over the next 90 seconds, at least with
underscore
‘s method.lodash
adds acancel
method to the returned function, which allows you to drop any currently delayed calls if you like.I’m pretty sure that’s not true. It’s certainly not true in the example he included.
You’re absolutely right, Jeremy T.
I’m not sure what I was thinking.