1

I have a set of functions that are run through the async.series() function which take as parameters the values that I happen to have stored in an array.

params = [ {id: 1, name: "Two tires fly. Two wail."},
           {id: 2, name: "Snow-Balls have flown their Arcs"},
           {id: 3, name: "A beginning is the time for taking the most delicate care"}
         ];
async.series(
[   function(callback) {
        myFunction(callback, params[0]);
    },
    function(callback) {
        myFunction(callback, params[1]);
    },
    function(callback) {
        myFunction(callback, params[2]);
    },
]);

Obviously the array is much larger and it would be convenient to wrap them into a loop:

var functionsArray = [];

for (var i = 0; i < params.length; ++i) {
    functionsArray.push(
        function(callback) {
           myFunction(callback, params[i]);
        }
     );
 }

 async.series(functionsArray);

Alas, this technique makes jshint freak out about defining a function in an array and I understand why it won't work. i will be a fixed value at call time and is not trapped as a value.

How do I create a set of functions whose parameters are in an array so that I don't have to explicitly define each one.

I am willing to use other facilities in async. Also, the functions are highly async, thus the reason for using the async.series()

2
  • I wanted to do this the other day and there didn't seem to be an elegant way. Might be something to add to async? Commented Jan 6, 2014 at 4:46
  • But looks like each() is what I needed. Commented Jan 6, 2014 at 4:54

2 Answers 2

1

You probably want async.each or async.eachSeries (the first works in parallel):

async.eachSeries(params, function(param, callback) {
  myFunction(callback, param);
}, function(err) {
  // Put code that needs to run when its all finished here
})

It could be even easier, though. If myFunction took parameters with the callback last (which is the norm in node.js), you could drop the extra anonymous function:

async.eachSeries(params, myFunction, function(err) {
  // Put code that needs to run when its all finished here
})

If you write your functions with callback-last signatures, they will be much more likely to play nicely with node.js libraries.

Alas, this technique makes jshint freak out about defining a function in an array and I understand why it won't work.

I assume you mean "defining a function in a loop"? If so, this is because defining functions is fairly expensive, and jshint discourages - not because it won't work. It may also have recognized that i will not be what you want - it will be the final value because of how javascript closures work. In general, you should avoid defining functions in a loop, but you can avoid the i issue with an IIFE (immediately-invoked function expression):

for (var i = 0; i < params.length; ++i) {
    (function(i) {
        functionsArray.push(
            function(callback) {
                myFunction(callback, params[i]);
            }
        );
    })(i);
}
Sign up to request clarification or add additional context in comments.

3 Comments

I will mark it correct but I think you meant to change the first two code samples to reflect your answer to use eachSeries rather than series.
@NathanielJohnson Yes, that is what I meant. Updated.
Good answer. I ended up using underscore's (OK lodash) _.map() to map the functions into an array. The reason for this wasn't stated in the question but I had an startup and shutdown function that I wanted to add into the function array, however this was the correct answer for what I asked.
1

You can localize 'i' by wrapping what you want in an anon. self-calling function:

params = [ {id: 1, name: "Two tires fly. Two wail."},
           {id: 2, name: "Snow-Balls have flown their Arcs"},
           {id: 3, name: "A beginning is the time for taking the most delicate care"}
];

var currentlyFinished = 0;
function finished () {
    currentlyFinished ++;
    if( currentlyFinished == params.length ) {
        //
        // you will end up here after all the params have been run thru your function
        // If you need a list sorted after all the callbacks, you'd do that here
        // 
    }
}
for (var i = 0; i < params.length; ++i) {
    (function(currentIndex) {  // 'i' becomes 'currentIndex', which is local to the anon. function
        myFunction( params[currentIndex], function () {
            finished ();
        });
    })(i);  // here is where you 'call' the anon. function, passing 'i' to it
 }

And as a note, I've never used the async lib., but it def. looks like it's a useful tool, with plenty of other methods that can probably do it in a simpler way; I do like to solve things my way first, to understand them, the libs can do the heavy lifting once I get the idea.

7 Comments

Thanks for the try but these functions are async and must be called in series. That is the reason for the async.series() call.
Just because something is asynchronous doesn't mean it has to be called in series. What exactly are you doing that requires asynchronous functions to be called in series? As for my example, there's no way to know what will finish before what, just when all are done. (to be clear)
Loading massive amounts of data that will crash the server if I run them too quickly. This is my way of throttling the load.
Why does currentIndex take on the value of i?
Because it's a function parameter. With the (i) at the end, currentIndex is passed the value of i. Also, check out async.eachSeries(arr, iterator, callback), that might be your ticket. github.com/caolan/async#eachSeries
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.