Skip to content

Commit

Permalink
Initial draft of _.throttle and _.debounce
Browse files Browse the repository at this point in the history
  • Loading branch information
jashkenas committed Nov 30, 2010
1 parent 6763b06 commit 9e3e067
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 11 deletions.
29 changes: 27 additions & 2 deletions test/functions.js
Expand Up @@ -54,19 +54,44 @@ $(document).ready(function() {
equals(fastFib(10), 55, 'a memoized version of fibonacci produces identical results');
});

asyncTest("functions: delay", function() {
asyncTest("functions: delay", 2, function() {
var delayed = false;
_.delay(function(){ delayed = true; }, 100);
_.delay(function(){ ok(!delayed, "didn't delay the function quite yet"); }, 50);
_.delay(function(){ ok(delayed, 'delayed the function'); start(); }, 150);
});

asyncTest("functions: defer", function() {
asyncTest("functions: defer", 1, function() {
var deferred = false;
_.defer(function(bool){ deferred = bool; }, true);
_.delay(function(){ ok(deferred, "deferred the function"); start(); }, 50);
});

asyncTest("functions: throttle", 1, function() {
var counter = 0;
var incr = function(){ counter++; };
var throttledIncr = _.throttle(incr, 50);
throttledIncr(); throttledIncr(); throttledIncr();
setTimeout(throttledIncr, 60);
setTimeout(throttledIncr, 70);
setTimeout(throttledIncr, 110);
setTimeout(throttledIncr, 120);
_.delay(function(){ ok(counter == 3, "incr was throttled"); start(); }, 180);
});

asyncTest("functions: debounce", 1, function() {
var counter = 0;
var incr = function(){ counter++; };
var debouncedIncr = _.debounce(incr, 50);
debouncedIncr(); debouncedIncr(); debouncedIncr();
setTimeout(debouncedIncr, 30);
setTimeout(debouncedIncr, 60);
setTimeout(debouncedIncr, 90);
setTimeout(debouncedIncr, 120);
setTimeout(debouncedIncr, 150);
_.delay(function(){ ok(counter == 1, "incr was debounced"); start(); }, 220);
});

test("functions: wrap", function() {
var greet = function(name){ return "hi: " + name; };
var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); });
Expand Down
9 changes: 0 additions & 9 deletions test/objects.js
Expand Up @@ -11,15 +11,6 @@ $(document).ready(function() {
});

test("objects: functions", function() {
var expected = ["all", "any", "bind", "bindAll", "breakLoop", "clone", "compact",
"compose", "contains", "defer", "delay", "detect", "each", "every", "extend", "filter", "find", "first",
"flatten", "foldl", "foldr", "forEach", "functions", "head", "identity", "include",
"indexOf", "inject", "intersect", "invoke", "isArguments", "isArray", "isBoolean", "isDate", "isElement", "isEmpty", "isEqual",
"isFunction", "isNaN", "isNull", "isNumber", "isRegExp", "isString", "isUndefined", "keys", "last", "lastIndexOf", "map", "max",
"memoize", "methods", "min", "mixin", "noConflict", "pluck", "range", "reduce", "reduceRight", "reject", "rest", "select",
"size", "some", "sortBy", "sortedIndex", "tail", "tap", "template", "times", "toArray", "uniq", "unique",
"uniqueId", "values", "without", "wrap", "zip"];
same(expected, _.methods(_), 'provides a sorted list of functions');
var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce};
ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object');
});
Expand Down
27 changes: 27 additions & 0 deletions underscore.js
Expand Up @@ -419,6 +419,33 @@
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};

// Internal function used to implement `_.throttle` and `_.debounce`.
var limit = function(func, wait, debounce) {
var timeout;
return function() {
var context = this, args = arguments;
var throttler = function() {
timeout = null;
func.apply(context, args);
};
if (debounce) clearTimeout(timeout);
if (debounce || !timeout) timeout = setTimeout(throttler, wait);
};
};

// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function(func, wait) {
return limit(func, wait, false);
};

// 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.
_.debounce = function(func, wait) {
return limit(func, wait, true);
};

// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
Expand Down

0 comments on commit 9e3e067

Please sign in to comment.