Hi I'm trying to create a synchronous loop using underscore js. For each loop iteration I make some further asynchronous calls. However, I need to wait until each iteration call is finished before I move on to the next iteration.
Is this possible in underscore js ? If yes, how so ? could someone please provide an example ?
_.( items ).each( function(item) {
// make aync call and wait till done
processItem(item, function callBack(data, err){
// success. ready to move to the next item.
});
// need to wait till processItem is done.
});
UPDATE
I solved this using the async.eachSeries method.
async.eachSeries( items, function( item, callback){
processItem(item, function callBack(data, err){
// Do the processing....
// success. ready to move to the next item.
callback(); // the callback is used to flag success
// andgo back to the next iteration
});
});
You can't use a synchronous looping construct such as underscore's .each()
because it won't wait for the async operations to be completed before going to the next iteration and it can't be made to do so in a single threaded world like Javascript.
You will have to use a looping construct that specifically supports async operations. There are many to choose from - you can build your own pretty easily or use the async library in node.js or let promises sequence things for you. Here's an article about some async control in underscore: daemon.co.za/2012/04/simple-async-with-only-underscore.
Here's one common design pattern I use:
function processData(items) {
// works when items is an array-like data structure
var index = 0;
function next() {
if (index < items.length) {
// note, I switched the order of the arguments to your callback to be more "node-like"
processItem(items[index], function(err, data) {
// error handling goes here
++index;
next();
}
}
}
// start the first iteration
next();
}
For pre-built libraries for node.js, the async library is quite often used for this. It has async-capable versions of many of the popular methods for iterating collections such a .map()
, .each()
, .reduce()
, etc... In your case, I think you'd be looking for .eachSeries()
to force the async operations to run one after another (and not in parallel).
For using promises, the Bluebird promise library has an async capable .each()
that calls the next iteration when a promise is resolved allowing you to use async operations in the iterator, but maintain sequential execution.
As an answer to your question: No, this cannot be done with underscore. All the items will be processed and you have no way to process the array as a serie.
you probably want to look at something like async/mapSeries