Underscore.js throttle vs debounce example
An example test visually demonstrating the difference between Underscore.js's throttle() and debounce(), the latter with the "immediate" parameter set true.
by rastrano
HTML
<p>Click the meters repeatedly. Events throttled/filtered by 1 second.</p>
<ul class="meter meter1">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
<p>
<code>_.throttle()</code> will fire once, and subsequent clicks will fire
only after 1 second has passed—but the subsequent click will happen.
<b>This is not good for filtering double-clicks.</b>
</p>
<p>
<code>_.debounce()</code> (with a <code>true</code> third parameter)
will <i>filter</i> subsequent clicks.
<b>This <i>is</i> good for filtering accidental clicks.</b>
</p>
CSS
p {
margin: 20px 20px 0px;
clear:both;
}
.meter {
margin: 10px 20px;
padding: 5px;
border: 1px solid #ccc;
border-radius: 4px;
font-family: sans-serif;
display: block;
float: left;
clear: left;
box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.2);
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.meter:hover {
background: #ff8;
cursor: pointer;
}
.meter li {
border: 1px solid #ccc;
background: #fff;
width: 25px;
height: 20px;
text-align: center;
padding-top: 8px;
float: left;
border-radius: 25px;
margin-right: 6px;
display: inline-block;
color: #ccc;
}
.meter li:last-child {
float: none;
margin: 0;
}
.meter li.on {
background: #2f2;
box-shadow: inset 0px 1px 0px 2px rgba(0,0,0,0.1);
border: 1px solid #777;
color: #000;
font-weight: bold;
text-shadow: 0px 1px #fff;
}
.meter1:after {
content: "_.throttle()";
padding: 0px 5px;
}
.meter2:after {
content: "_.debounce()";
padding: 0px 5px;
}
.blink:after {
font-weight: bold;
}
JavaScript
// A (possibly faster) way to get the current timestamp as an integer.
_now = Date.now || function() {
return new Date().getTime();
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = _now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
$(function () {
function changeMeter (ev) {
var $ul = $(ev.target).closest("ul"),
numLi = $("li", $ul).length,
$on = $("li.on", $ul),
numOn = $on.length;
if (numLi == numOn) {
// All filled! Reset!
$on.removeClass("on");
} else {
// Add a new $on
if (numOn == 0) {
$("li:first", $ul).addClass("on");
} else {
$("li.on:last",...