Underscore.js

Wednesday, August 17, 2011

Liam told me about Underscore.js some time ago. From the home page:

Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects. It's the tie to go along with jQuery's tux.

The functional programming support from Underscore includes, but is not limited to, the standard map, filter, reduce functionality.

var data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var result =
     _.filter(data, function (n) { return n % 2 == 0; })
      .map(function (n) { return n * n; })
      .reduce(function (sum, n) { return sum += n; });

Notice the gateway to all the Underscore features is the  _ (underscore) object.

What I've found useful recently are the throttle and debounce methods. Throttle ensures a method is only called once every n milliseconds, while debounce will ensure a method is only called after someone stops calling the method for n milliseconds. I'm fond of debounce because I spent many hours years ago writing debounce logic for mechanical switches, but also because it demonstrates the power of composing behavior with higher order functions. Both throttle and debounce use an internal function called limit to implement their behavior (the source code from Underscore shown below, sans comments).

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);
    };
};

_.throttle = function (func, wait) {
    return limit(func, wait, false);
};

_.debounce = function (func, wait) {
    return limit(func, wait, true);
};

There are so many interesting concepts in the limit function – I think you could build an 8 hour workshop from those 11 lines of code.

To see how throttle would work, you could use the following code to process and log mouse movement, but throttle the processing so you only see the console output once every 1,000 milliseconds.

$(window).mousemove(_.throttle(function (e) {
    console.log(e.pageX, e.pageY);
}, 1000));
Fantastico!

Comments
gravatar Dan G. Switzer, II Thursday, August 18, 2011
I have 2 major issues with the Underscore implementation of the throttle:

1. It delays execution of the first hit until after the delay. IMO, throttle() should execute on the first hit and then wait for the delay before executing again.

2. The arguments passed to your throttled event are based on the arguments of the first hit. So, in your example the mouse coordinates are based on where the cursor was when you first moved your mouse. This really becomes problematic when you want to update something on the screen w/where the cursor is in it's resting state.

For example, here's your example:
http://jsfiddle.net/MNGpr/

If you start the cursor in the upper left and move it rapid to the bottom left, the coordinates that appear in the output will be the top left. This is probably not the behavior you want.

So, I use a throttle function I wrote:
http://jsfiddle.net/XjZUC/

What it does is fire off a throttle() event on first execution. This improves event handlers based on mouse/scrolling input because you don't wait for the initial delay to see the results. It also makes sure to use the last set of arguments passed into the function for updating. This fixes the behavior of the mouse coordinates showing coords closer to where the cursor actually is at the point of execution.
gravatar scott Thursday, August 18, 2011
@Dan: Thanks for taking the time to write such a detailed comment. I can see where throttle will create a problem.
gravatar Dan G. Switzer, II Thursday, August 18, 2011
@Scott:

I just blogged this too:
blog.pengoworks.com/...
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!