Passing arrays of functions to async parallel - javascript

I have to pass an array of functions to the async.js module for node.js.
The normal way from the docs would be:
async.parallel([
function(callback){
setTimeout(function(){
callback(null, 'one');
}, 200);
},
function(callback){
setTimeout(function(){
callback(null, 'two');
}, 100);
},
],
// optional callback
function(err, results){
});
I tried like this:
for(var i = 0; i < jsonData.length; i++)
{
...
o.url = serviceurl;
o.title = jsonData[i];
var ff = function(callback){
obj.loadService(o.title,o.url,callback);
}
callItems.push(ff(function(){return true;}));
}
async.parallel(
callItems,
// optional callback
function(err, results){
console.log('all calls called without any errors');
}
);
That runs through but the main callback isn't called. And so I can't say if all parallel calls are done.
What am I missing here?

It looks like the closures are improperly formed in the for loop. Try an external function that returns the value you're currently assigning to ff. Example:
for(var i = 0; i < jsonData.length; i++)
{
...
o.url = serviceurl;
o.title = jsonData[i];
var ff = makeCallbackFunc(obj, o.title, o.url);
callItems.push(ff(function () {return true;}));
}
function makeCallbackFunc(obj, title, url) {
return function (callback) {
obj.loadService(title, url, callback);
};
}
I'm a bit confused by what you are adding to callitems - namely the result of calling ff with the function parameter - it won't be a callback, it will execute right away.

Related

async whilst not running fn

I want to use async.whilst function and probably missing something badly when I'm getting just the first console.log on the output.
// app.js file
var async = require('async');
var count = 0;
async.whilst(
function () {
console.log('first')
return count < 5;
},
function (callback) {
count++;
console.log('second')
callback()
},
function (err) {
console.log('third')
}
);
// run the script
$ node app.js
first
$
Have a look at the documentation: you need a callback also for the first function
var async = require('async');
var count = 0;
async.whilst(
function (callback) {
console.log('first')
return callback(null, count < 5);
},
function (callback) {
count++;
console.log('second')
callback()
},
function (err) {
console.log('third')
}
);
You should use a callback inside your first function, async make calls to subsequent function when the callback gets invoked. your code should be
async.whilst(
function (cb) {
console.log('first')
cb(null,count < 5);
},
function (callback) {
count++;
console.log('second')
callback()
},
function (err) {
console.log('third')
}
);

node async waterfall using an array with callbacks that have arguments

I am getting an error that I do not understand. I am calling async.waterfall with an array of functions. The function is 'shortened' for clarity.
FabricCommand.prototype.do = function (callback, undoArray) {
var self = this;
if (undoArray === undefined) {
undoArray = [];
}
undoArray.push(self);
callback(null, undoArray);
};
I create the array as listed below: doCommands is an array and the objects are added as such:
doCommands.push(fabricCommand.do.bind(fabricCommand));
the waterfall setup:
async.waterfall(
doCommands,
function(err, undoCommands){
if (err) {
// do something ...
}
else {
console.log('we succeeded with all the do commands... and there are '
+ undoCommands.length
+ ' in the undoCommands but we will disregard it...');
}
}
);
Now when I run this code, the first time through the FabricCommand.do function, I allocate the undoCommands array and I add one to it, next time through I get, where I try to add the array element, the following error:
undoArray.push(something);
^ TypeError: Object function (err) {
if (err) {
callback.apply(null, arguments);
callback = function () {};
}
else {
var args = Array.prototype.slice.call(arguments, 1);
var next = iterator.next();
if (next) {
args.push(wrapIterator(next));
}
else {
args.push(callback);
}
async.setImmediate(function () {
iterator.apply(null, args);
});
}
} has no method 'push'
Can anyone see what I am doing wrong?
The function that is executed by async.waterfall must have the following signature:
function(arg, callback) { … }
or, with multiple arguments:
function(arg1, arg2, callback) { … }
In your case, you simply inverted the two parameters:
FabricCommand.prototype.do = function (callback, undoArray) { … }
callback received the value intended to be stored in undoArray, and undoArray received the value intended for the callback, i.e. a function: that's why you encountered this weird error (function […] has no method 'push').
You need to put the parameters in the correct order:
FabricCommand.prototype.do = function (undoArray, callback) { … }
A second issue is that the first function of the waterfall receives only one parameter: the callback (because there is no value to be received, as it is the first function of the waterfall). A solution is to check the number of arguments:
if (Array.prototype.slice.apply(arguments).length === 1) {
callback = undoArray;
undoArray = undefined;
}
Here is a working gist.

I am using Async's Library Parallel method wrong? But how?

I am using the async library's parallel function, but I am running into a weird problem. I loop through an array of objects to create an array of functions called people. People gets passed into async's parallel method to execute in parallel. However, when I take a look at the parameter "people" that actually get passed into the "printFunction" it is wrong. The people parameter is always the last object in my people array.
So in this example, the console log in printFunction would print
{ 'name' : 'jar jar binks' }
{ 'name' : 'jar jar binks' }
Code:
var people = [{ 'name' : 'obi wan kenobi'}, { 'name' : 'jar jar binks' }];
// Create an array of tasks to be processed.
for(var i = 0; i < people.length; i++) {
tasks.push( function(callback) {
setTimeout( makePrintFunction(people[i], callback), 200);
});
}
// Process those tasks.
async.parallel(tasks, function(err, stuff) {
// ...do some stuff with the returned array here.
});
Make Print Function:
function makePrintFunction(people, next) {
return validateAndTrackFlag(people, next);
}
function printFunction(people, next) {
console.log(people); // Always prints: { 'name' : 'jar jar binks' }
next(null, true)
}
Obviously this is not the real code, I just changed the names and objects.
Thanks for any help in advance.
This is not an async problem, it's a closure problem.
Try this:
for(var i = 0; i < people.length; i++) {
(function (i){
tasks.push( function(callback) {
setTimeout( makePrintFunction(people[i], callback), 200);
});
})(i)
}
Or this:
people.forEach (function (p){
tasks.push( function(callback) {
setTimeout( makePrintFunction(p, callback), 200);
});
});
You are dealing with classical scoping issue. Try this:
for(var i = 0; i < people.length; i++) {
(function(i) {
tasks.push( function(callback) {
setTimeout( makePrintFunction(people[i], callback), 200);
});
})(i);
}
or even better
var create_scope = function(i) {
tasks.push( function(callback) {
setTimeout( makePrintFunction(people[i], callback), 200);
});
};
for (var i = 0; i < people.length; i++) {
create_scope(i);
}
or the best:
people.forEach(function(person) {
tasks.push( function(callback) {
setTimeout( makePrintFunction(person, callback), 200);
});
});
This happens because for loop does not create a scope, i.e. when functions are fired i is the last index. Now functions do create scope, so inside anonymous function i is the expected value.

Java script modify function at run time

enter code hereI have the following code
function a(){alert("a");}
I want to create a function b as
function b(){alert("a"); alert("b");}
My approach is something like
var b = a + alert("b");
This is of course not working. But I am wondering if there is some kind of library supporting this.
Edit: Maybe I need to describe my scenario so that its more clear what I want to achieve.
I am using async.js library to handler multiple async calls. My code looks like
var values = {};
...
function all() {
var allDfd = $.Deferred();
async.parallel(
[function (callback) {
remoteCall(function (result) {
values.v1 = result;
callback(null, 'one');
});
},
function (callback) {
remoteCall(function (result) {
values.v2 = result;
callback(null, "two");
});
},
function (callback) {
remoteCall(function (result) {
values.v3 = result;
callback(null, "three");
});
}], function (err, results) {
allDfd.resolve();
});
return allDfd.promise();
}
Clearly there are a lot of repetitive code that bothers me. So my idea is to create a function asyncCall to perform the boilerplate tasks. The idea is
var values = {};
...
function all() {
var allDfd = $.Deferred();
function getAsyncCall (func, innerCallback, callback) {
return function asyncCall(func, innnerCallback, callback){
func(innerCallback + callback(null)); // combine innerCallBack and callback code
}
}
async.parallel(
[getAsyncCall(remoteCall, function(result){values.v1=result;},callback),
getAsyncCall(remoteCall, function(result){values.v2=result;},callback),
getAsyncCall(remoteCall, function(result){values.v3=result;},callback),
], function (err, results) {
allDfd.resolve();
});
return allDfd.promise();
}
The line with the comment is what I am pondering. I am trying to create a new function that combines inner and outer callbacks.
You can do
var b = function() { a(); alert('b'); }
You could write:
var a=function(){alert("a");}
var b=function(){a(); alert("b");}
And to go a little further, you can even write a whole function composition function:
function compose( functions ) {
return function(){
for(var i=0; i!=functions.length; ++i) {
functions[i]();
}
};
}
var c=compose( [a, function(){ alert("b"); }] );
(See it at work at http://jsfiddle.net/xtofl/Pdrge/)

Async calling out generated functions in series

I am having problem with calling the generated functions in serial. I am using the async library and the code seems to work when there is no deep callback calling needed. When I add the real scenario it throws errors.
Here is the example which works, returns the array of 0 to 4:
Scrape.prototype.generatePageFunctions = function() {
var functionList = new Array(), self = this;
for (var i = 0; i < this.pageSet; i++) {
(function(i) {
functionList.push(function(cb) {
// Inner functions which will be called in seriers
var timeoutTime = parseInt(Math.random() * 5000 + 3000, 10);
setTimeout(function() {
self.setIndex(i);
//self.getSite(function)
cb(null, i);
}, timeoutTime);
});
})(i);
}
return functionList;
}
Scrape.prototype.run = function() {
var functionList = this.generatePageFunctions();
async.series(functionList, function(err, results) {
console.log('Job is completed ');
console.log(results);
});
}
Now adding the real scenario like downloading the site and then put in callback:
Scrape.prototype.generatePageFunctions = function() {
var functionList = new Array(), self = this;
for (var i = 0; i < this.pageSet; i++) {
(function(i) {
functionList.push(function(cb) {
// Inner functions which will be called in seriers
var timeoutTime = parseInt(Math.random() * 5000 + 3000, 10);
setTimeout(function() {
self.setIndex(i);
self.getSite(function(result) {
// Async callback to pass the data
cb(null, result);
});
}, timeoutTime);
});
})(i);
}
return functionList;
}
Error is like this, even if passing instead of result iterator variable i:
/home/risto/scrape/node_modules/async/lib/async.js:185
iterator(x.value, function (err, v) {
^
TypeError: Cannot read property 'value' of undefined
at /home/risto/scrape/node_modules/async/lib/async.js:185:23
at /home/risto/scrape/node_modules/async/lib/async.js:108:13
at /home/risto/scrape/node_modules/async/lib/async.js:119:25
at /home/risto/scrape/node_modules/async/lib/async.js:187:17
at /home/risto/scrape/node_modules/async/lib/async.js:491:34
at /home/risto/scrape/scraper/scrape.js:114:13
at /home/risto/scrape/scraper/scrape.js:64:16
at Object.<anonymous> (/home/risto/scrape/scraper/engines/google.js:58:12)
at Function.each (/home/risto/scrape/node_modules/cheerio/lib/api/utils.js:133:19)
at [object Object].each (/home/risto/scrape/node_modules/cheerio/lib/api/traversing.js:69:12)
// Edit
Only result which gets added into the complete callback is the first one, other functions are never called.
Also for information the functions return object literals if that does matter.
There is nothing wrong with your code. Creating a simple testcase shows that.
I created a mock:
Scrape = function() {
this.pageSet = 5;
}
Scrape.prototype.setIndex = function() {
}
Scrape.prototype.getSite = function(cb) {
cb('works');
}
and calling the run method it outputs the expected:
[ 'works', 'works', 'works', 'works', 'works' ]
So the problem is somewhere else. Have you tried to check the functionList variable in your run method?
Thank you #KARASZI István, all the code above is correct, the problem seemed in somewhere else. The deepest callback got called multiple times but the outer one got called only once.

Categories