JavaScript Debounce Function
One of the biggest mistakes I see when looking to optimize existing code is the absence of the debounce function. If your web app uses JavaScript to accomplish taxing tasks, a debounce function is essential to ensuring a given task doesn't fire so often that it bricks browser performance.
For those of you who don't know what a debounce function does, it limits the rate at which a function can fire. A quick example: you have a resize listener on the window which does some element dimension calculations and (possibly) repositions a few elements. That isn't a heavy task in itself but being repeatedly fired after numerous resizes will really slow your site down. Why not limit the rate at which the function can fire?
Here's the basic JavaScript debounce function (as taken from Underscore.js):
// Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; };
You'll pass the debounce function the function to execute and the fire rate limit in milliseconds. Here's an example usage:
var myEfficientFn = debounce(function() { // All the taxing stuff you do }, 250); window.addEventListener('resize', myEfficientFn);
The function above will only fire once every quarter of a second instead of as quickly as it's triggered; an incredible performance boost in some cases.
I'm often asked what rate should be used when debouncing, and it's an impossible question to answer because it depends on the task. The best way to know is testing different rates yourself and seeing where you notice a slowdown -- if you notice it, so will your users!
Never heard of this technique and it makes completely sense! Your example of a resize event is probably the best example to understand the importance of such a technique!
Thanks a lot for the tip, I’ll make sure to use when needed next time!
The other example is scroll (
onScroll
Event) which is often fired a lot of times on some website.the guys from Loop Infinito have an awesome article about debounce (and throttle), in portuguese:
http://loopinfinito.com.br/2013/09/24/throttle-e-debounce-patterns-em-javascript/
Didn’t you write about this 2 years ago? http://davidwalsh.name/function-debounce
Yes, but (1) this one’s shorter and (2) people seem to have though you needed a JS toolkit to make it happen. I’m clarifying the minimal requirement here.
This is an amazing vanilla explanation and very much appreciated. Transformed my multi-line approach with a
setTimeout
filtering the last fire – to a logical place. √√√Nice! Also worth mentioning the additional benefit of combining the debounce with requestAnimationFrame (IE10+)—there’s a nice write-up on usage and applications over at http://www.html5rocks.com/en/tutorials/speed/animations/.
You can also use this to prevent client to overwhelm a web service.
Ex: an instant search function. You don’t want a HTTP call whenever every single key is pressed, but you’ll want to wait the user to end typing (a few hundreds ms should be enough) before sending the HTTP request.
To clarify, this only fires myAwesomeFunc() once every 250ms,it doesn’t queue it up fifty times?
With, that made little sense. Sorry about that!
What I want to ask is whether the debounce simply queues up function calls or if it actually cancels any subsequent calls within the timeframe (i.e. 250ms).
Correct — any calls during the wait period are ignored.
I use to define a debounce method on
Function.prototype
for this. Very handy.Just to clear this out:
If myEfficientFn is called while waiting it will re start the timeout, so it will be possible to delay forever the execution of that function by calling it in periodic basis.
Also, in this line:
Why are you asking for !timeout ?
Would this timeout be different to null every time unless wait is 0?
It might be worth noting the difference between throttling and debouncing, as the terms are often confused. There is a subtle, but important, difference between the two.
A debounced function is called only once in a given period, delay milliseconds after its last invocation (the timer is reset on every call). Whereas a throttled function is limited to be called no more than once every delay milliseconds.
It’s easiest to understand with an example:
The throttled function will be called 4 times because throttling guarantees the callback will only be invoked at most once every delay milliseconds. However the debounced function will only be called a single time, because it resets its internal timer on every call, and so executes 250ms (the supplied delay) after the last call (1 second in)
hi, could you share/link to a code sample of the throttling function? i’m eager to compare it with article’s debounce().
Remy Sharp has good examples of both here: http://remysharp.com/2010/07/21/throttling-function-calls
The difference between the two is slight, and which to use depends on your needs.
Throttled can be simply achieved by changing the way the timeout is cleared/set. eg..
Note that the timeout is not destroyed and recreated for every call (unlike debouncing). Its destroyed after the timeout has completed and created on the first call after its been destroyed.
I enjoyed reading this post. I’m a c#, ASP.NET, and C++ programmer with more than 3 years of experience in Web designig but not heard of this technique before.
Hi, what a great piece of coding, I am trying to use the flip so that it starts automatically with java when I open a page in an epub html, however I can’t seem to integrate the flip coding with javascript. I need to remove the hover effect and replace it with javascript function to run the html page on opening , can you help as I must be doing something basically wrong here,
Tks Ron Ritter
<pre Ron Ritter February 5, 2014
Hi, what a great piece of coding, I am trying to use the flip so that it starts automatically with java when I open a page in an epub html, however I can’t seem to integrate the flip coding with javascript. I need to remove the hover effect and replace it with javascript function to run the html page on opening , can you help as I must be doing something basically wrong here,
Tks Ron Ritter
Have been using this pattern for sometime without knowing that it is called “debounce” :)
http://stackoverflow.com/a/21382048/87015
You will also find this implemented in jQuery UI autocomplete widget.
Loved the 160bytes minified version, very useful for projects without Underscore and Lodash!
Lo-Dash as a more complete/complex version also:
http://lodash.com/docs#debounce
This is helpful, as I often bind event handlers to window resize in order to handle some responsive layout on resizing (like, sticky elements). Most of the times the resizing tends to be slow, mainly because of overcalling the event handler.
this debounce function comes in handy to fix that.
You should also consider creating a version of debounce that utilizes requestAnimationFrame() (if available) for even better performance.
The reason it’s called “debounce” is because it comes from a physical situation which literally has things bouncing:
http://en.wikipedia.org/wiki/Switch#Contact_bounce
How do you handle the scope of the function that is being debounced?
Was having some issues getting debounce to initialize an immediate function call.
Your code finally makes sense. I couldn’t see the logic of setting up a timeout and checking it’s value right after it.
I came here to report the same bug which Tom mentioned – the older version at http://davidwalsh.name/function-debounce uses a temporary variable so it can do the
immediate && !timeout
check before callingsetTimeout
.I’ve updated the post to fix said issue. Thanks everyone!
I’ve seen so many websites goes completely weird while not using debounce technique, especially some of them that involved animations.
Only the semicolon at the end isn’t neccesary ;)
Isn’t there a huge problem with this debounce? -Multiple events
Say event1 calls debounce with func1,
then event2 comes along and calls debounce with func2. (before func1 is fired).
The events are different so both functions should fire, won’t debounce will only fire func2?
^nevermind :) I was wrong.. Tried to use as a self-invoking function, that’s why it didn’t work properly for me
Nice technique, mr. Walsh. I’m going to see whether this reduces the initialisation lag in one of my word games.
Why do you need immediate param? What’s the difference between setting wait to 0 and assigning true to immediate?
Exactly my point. The immediate is of no use, specially if the implementation above returns a function.
If the
debounce
method is changed to immediately exec, instead of returning a value, theimmediate
param can be put to use.It’s not clear from your code how one would use
immediate
If
immediate
is passed right at the debounce call, then what’s the use of a debounce function.Either you are missing an IIFE (Immediately invoked function expression call) or the intention of your code is unclear.
Since your code returns a function, the returned function should have the ability to immediately execute if immediate is passed true. I don’t see this feature
This looks like you are storing the return value of the debounce function in some other variable.
for eg. (using your impl.)
By using immediate, you execute the function at the beginning of the interval rather than after. You would likely use it in conjunction with an event that fires very often where you may want to limit the calls to your function for performance (e.g. scroll, resize). The immediate parameter gives you flexibility over whether you want to execute before setting the timer, or after it completes.
e.g.
On scroll handler with immediate + 500ms wait
*user begins scrolling* – *execute fn* – wait 500ms – *execute fn*
vs on scroll handler with no immediate + 500ms wait
*user begins scrolling* – wait 500ms – *execute fn* – wait 500ms
I don’t understand why there is a variable arguments in line #8. Where is that defined?
Nevermind. I found out that there is an arguments object for functions. JSLint was complaining about this not being defined.
Hello, I’m still having a tough time grokking what’s really going on here, as I’m not really proficient in JavaScript so much as I am competent. For a young developer like myself, would you be willing to do a line-by-line breakdown of what’s going on? I’m usually pretty quick on the uptake, but could really use a hand here. Thanks :)
Thanks a lot for this tip! Helped me fix the slow performance on one of my apps.
Wondering if it can be written as below mentioned.
for
immediate=true
, the second call would execute immediately, without debouncing.it not run with jquery-2.1.1.min.js???
i have just test it in 2.1.1 . it dont run.
please check http://sieuthimayphatdien.com.vn/may-phat-dien
Can later function be moved outside returned function scope, like this:
If the later function is moved outside the returned function scope, no arguments passed to the returned function will be available to it.
It worked but I had to move
timeout
to the parent scope (global in my case) to make it work.Indeed I haven’t attach
debounce
to an event but to a return event of jQuery Tooltipcontent()
method.I still dont get when is useful this function, can someone clarify when will be a good idea use this code? I made a simple resize function, and using or not debounce is not a big difference…
https://jsbin.com/jewefacoce/edit?js,console,output
Brilliant, the more I read on your blog, the less I am dependent on jquery.
1) How can you do
at a location where the value of the variable
timeout
may be undefined? Please explain any assumptions and prerequisites for using the provideddebounce
function.2) In the provided example of how to use the
debounce
function, that function is called with two arguments (a function and a duration). But thedebounce
function is defined to take three parameters, not two. Please explain why there is a difference in the number of arguments.Which book must i read to know some of these powerful tricks? Or what should i look for? Thank you David!
Really great tip to boost client side performance and site lag issues. Thanks!
I am wondering whether it matters that you assign
timeout = null
in the later function. Why notundefined
?I think the description of the function in your article ought to be changed. Your code resembles a debounce, but the text suggest a throttle.
“The function above will only fire once every quarter of a second instead of as quickly as it’s triggered”
That’s not really what happens. The function in your example will never be executed as long as the resize event handler fires faster than 250 ms, but rather 250 ms after you stop resizing.
Hi – My background is actually in Ad Ops, not web development. But, I’m working on a site this has debouncing in its googletags. It makes the ads resize if the browser width narrows, but it also appears to be making multiple ad requests per ad slot. Does that make any sense?
Yeah I’m not sure this is debounce either. It has strange behaviour like if I hold down a key it doesn’t spit one event out every n ms, even if I’m still holding down the key after the debounce duration (my understanding of debounce).
Also the default to non-immediate is a bit sketchy for input (the main thing I’d use a debounce for).
What was the original use-case?
ignore that last comment, looks like I’m coming at this from a skewed angle.
There’s a lot going on in this function. Context and
args
andapply()
.When searching for “js debounce” this is the top entry. It would be really great if you could do a line by line breakdown and explanation for dummies.
Hi,
I hope you can reply me. I have a question.. I need to do a parallax effect in 50 element.. For doing it, Im using the
$(window).scroll(function())
If an element is visible in the screen section in the stage, I active a specific setInterval who will be destroied if the element is too up or too down. I’m using setInterval because the parallaxed image have a friction movement
Is this solution correct or it require too client resource? Is better the debounce method?
Mind blown.
You rock man, I just used this code in 2018!
I was so confused by this because you are clearing the timeout on every function call but that doesn’t match your description for what this function does. Luckily, a little searching led me to find out what was going on here.
“For those of you who don’t know what a debounce function does, it limits the rate at which a function can fire… Why not limit the rate at which the function can fire?”
This is what a throttling function would do. A debounce function completely halts function calls until the call rate of the function falls low enough. That’s a different thing from “limiting the rate at which a function can fire”.
“The function above will only fire once every quarter of a second instead of as quickly as it’s triggered; an incredible performance boost in some cases.”
This is an inaccurate/misleading explanation. With the code you provided, the function will only fire a single time while the trigger rate is above 4/second, not fire every .25 seconds while being triggered. If someone is non-stop triggering the function faster than 4 times per second, the function will only fire a single time at either the start of end of the rapid triggering depending on what immediate value is passed (because the timeout is being cleared on every function call). You’re showing the code for a debounce function but describing it as if it’s a throttling function.
I wanted to put the same comment here and saw that you already have. Whats interesting is that the comment/description with the original piece of code from underscore actually has the right & fantastic explanation of debounce, and nothing more needs to be added to it.
I came up with a similar function below that combines debouncing with throttling. The registered callback function also receives information about which type of debounce/throttle call it is receiving, which allows the callback to do things such as apply a class or style at the ‘start’ (e.g. “overflow: hidden” so scrollbars don’t appear during a resize), do some minimal UI updates ‘during’, and then finalize changes at the ‘end’. Both debouncing and throttling are optional (e.g. if you just want debouncing, pass 0 for throttlewait).
Note that certain UI updates (e.g. resizing and repositioning DOM elements) in a setTimeout/setInterval callback causes problems in some browsers (e.g. Chrome for Android) where the browser will halt execution of that setTimeout/setInterval until the browser decides enough time has passed – the delay before resuming is approximately 3 seconds even if you are being a responsible citizen using a debouncer function. If you use the function above, your callback should call window.requestAnimationFrame() with a function that makes the actual UI change(s) so that the setTimeout/setInterval calls of the debouncer/throttler continue to operate unhindered.
IIFE version for inline functions
Improved.
Your improved version not working with immediate option. Fixed
why we need to set timeout = null; inside setTimeout
yup, the function is a little bit hard to see the logic behind it. setting timeout = null so callnow is true and the callback func get called after the delay.
this might make more sense to you.
https://dev.to/monaye/refactor-davidwalsh-s-debounce-function-5afc
Here is another one with ES6 from
https://dev.to/monaye/refactor-davidwalsh-s-debounce-function-5afc
Thank you, David, I found this very useful. I wanted to give feedback from my experience using your snippet. I actually found it more readable to have debounce call a named function inside the addEventListener declaration:
Half a dozen, six of another, but I like having the bulky function declared after the event binding which you can’t do with a variable/function expression.
Thanks again, this is great!