Improved JavaScript debouncer
Debouncer functions are used as rate limiters for functions that trigger many times in rapid succession. This is an attempt to improve the pattern by triggering the function earlier, making it seem more responsive.
To quote css-tricks.com:
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.”
Existing approaches
Some popular implementations are Ben Alman’s or Remy Sharp’s.
The problem with the existing approaches is that they trigger only on the trailing end. Your function will trigger when the event has stopped firing for the configured time.
For example, using a debouncer on the keyup
event will only trigger the function once you stopped typing. It will not fire when you start typing.
Improved debouncer
This improved debouncer triggers the first call immediately, and only debounces the next calls. It will trigger again on the trailing end only if it detects more calls.
function debounce (fn, delay) {
var cooldown = null
var multiple = null
return function () {
var self = this
var args = arguments
if (cooldown) {
multiple = true
clearTimeout(cooldown)
} else {
fn.apply(self, args)
}
cooldown = setTimeout(function () {
if (multiple) {
fn.apply(self, args)
}
cooldown = null
multiple = null
}, delay)
}
}
Here’s a simple demo of the debouncer in action:
Performance
For performance, we could refactor the code to set the timeout only once. After testing both approaches with 10.000 debounced function calls, the difference between the version setting the timeout once and the one setting it multiple times is around ~50ms.
This is calculated using console.time
, starting when we begin calling the functions, and ending when the last debounced function is called.
Based on these results, I would say optimizing the debouncer to only set the timeout once, thus making it more verbose, is not worth it.
Here’s the small performance test I used. Make sure you have the console open (for console.time
).
lodash
The debouncer in lodash has a leading
option that makes it behave like this improved debouncer, but is much more complex.