1

I'm using UnderscoreJs with nodejs and have a need for the _.times() method. times() will invoke a function X number of times

This works as expected, however I need to iterate in a series, instead of in parallel which this appears to be doing.

Any idea if there's a way to use this in series w/ callback methods?

4

2 回答 2

3

Given something like this:

function f() {
    some_async_call({ callback: function(err, results) {...})
}
_(3).times(f);

Then the three f calls will happen in series but the some_async_call calls won't necessarily happen in series because they're asynchronous.

If you want to force your calls to run in series then you need to use the callback on the async call to launch the next one in the series:

function f(times, step) {
    step = step || 0;
    some_async_call({
        callback: function(err, results) {
            // Do something with `err` and `results`...
            if(step < times)
                f(times, step + 1);
        }
    });
}
f(3);

That approach will execute the three some_async_calls in series but, alas, the initial f(3) will return immediately. One solution to that problem is, of course, another callback:

function f(from_n, upto, and_finally) {
    some_async_call({
        callback: function(err, results) {
            // Do something with `err` and `results`...
            if(from_n < upto)
                f(from_n + 1, upto, and_finally);
            else
                and_finally();
        }
    });
}
f(0, 3, function() { console.log('all done') });

Where does _.times in with all this? No where really. _.times is just a for loop:

_.times = function(n, iterator, context) {
  for (var i = 0; i < n; i++) iterator.call(context, i);
};

_.times exists for completeness and to allow you to add for loop when using _.chain. You could probably shoe-horn it in if you really wanted to but you would be making a big ugly mess instead of simplifying your code.

You could use 250R's async idea but you'd have to build an array of three functions but _.range and _.map would be more appropriate for that than _.times:

// Untested off the top of my head code...

function f(callback) {
    some_async_call({
        callback: function(err, results) {
            // Deal with `err` and `results`...
            callback();
        }
    });
}

var three_fs = _(3).range().map(function() { return f });
async.series(three_fs);

But you still have to modify f to have a callback function and if you're always calling f three times then:

async.series([f, f, f]);

might be better than dynamically building the array with _.range and _.map.

The real lesson here is that once you get into asynchronous function calls, you end up implementing all your logic as callbacks calling callbacks calling callbacks, callbacks all the way down.

于 2012-05-08T17:26:51.520 回答
0

This async library might get you started https://github.com/caolan/async#series

Or if you want to do it yourself, the idea is to do recursive calls after each function callback is called, here's the source code https://github.com/caolan/async/blob/master/lib/async.js#L101

于 2012-05-08T01:22:11.420 回答