documentcloud/underscore — pull request #1145 by johnomalley (2013-06-09 19:44:56+00:00)
Title: Optimize _.debounce(fn, 0)

I find _.debounce immensely useful, but perhaps the most useful application is with a wait of 0. This simply allows a flood of events to be cascaded into one event handler. For example:

```javascript
// a backbone collection with a lot of models
// second argument to _.debounce isn't technically needed
// (jasmine 1.3.1 mock clock needs it)
collection.on("change", _.debounce(handleChangeEvent, 0));
// models fire a ton of events; handleChangeEvent invoked only once
changeAllOfTheModelsInCollection();
```

The problem is that _.debounce isn't optimized very well for this situation. Every invocation will call clearTimeout/setTimeout. To be sure these native methods are fast (at least in Chrome) but the performance hit is not insignificant, as the script below illustrates.

My output from this script:

_.debounce: 609ms
optimized debounce: 4ms

```javascript
(function() {
var _ = window._;
var console = window.console;

function optimizedDebounce(fn) {
var pending = false;
return function () {
if (!pending) {
var context = this, args = arguments;
pending = true;
setTimeout(function () {
pending = false;
fn.apply(context, args);
}, 0);
}
};
}

function elapsedTime(fn) {
var start = new Date();
fn();
return (new Date().getTime() - start.getTime()) + "ms";
}

function speedTest(debounceFn) {
var invocations = 99999;
var debounced = debounceFn(_.identity, 0);

return elapsedTime(function () {
for (var i = 0; i < invocations; i++) {
debounced();
}
});
}

console.info("_.debounce: " + speedTest(_.debounce));
console.info("optimized debounce: " + speedTest(optimizedDebounce));

}());
```