Is there something wrong with my code?, I use async.eachSeries but my result always throw undefined.
Here my code :
async.eachSeries([1,2,3], function(data, cb) {
setTimeout(function() {
cb(null, data+1);
}, 1000);
}, function(err, result) {
console.log(err, result);
});
My log returned : null, undefined instead of null, [2,3,4]
thanks... and sorry for my terrible english XD
The second argument is called when the iteration is done and with eachSeries(), it takes only one parameter, err. If you want result, you have to use mapSeries:
async.mapSeries([1, 2, 3],
function (data, cb) {
setTimeout(function () {
cb(null, data + 1);
}, 1000);
},
function (err, result) {
console.log(result);
}
);
You can use a form of result with eachSeries() as well:
var result = [];
async.eachSeries([1,2,3], function(data, cb) {
setTimeout(function() {
result.push(data+1);
cb(null);
}, 1000);
}, function(err) {
console.log(err, result);
});
That should work, although I'm not able to test it myself at the moment.
Related
I can't find a proper explanation of how to use the retry function from the async librarydocs
var async = require('async');
function callFunc(data, time, name, callback) {
console.log("#")
callback({message: data, time, name}, null); //error
// callback(null, {message: "ok"}); // ok
}
var func = callFunc.bind(null, "data", "time", "name", function (err, data) {
console.log(data);
return err;
})
async.retry({times: 3, interval: 1000}, func, function (err, results) {
console.log('===================================');
console.log('Async function');
})
Example of what I implement(but simpler). Can you tell me what I'm doing wrong? Killed the whole day for this.
P.S. The function should be called three times on error.
The async.retry function expects a function that has the signature function (callback, results), where callback is a callback function that you should call when the operation is complete, and results is an object that contains the results of the operation so far.
var async = require('async');
function callFunc(data, time,callback) {
console.log(data, time)
callback({message: "err"}, null); // error
// callback(null, {message: "ok"}); // ok
}
async.retry({times: 3, interval: 1000}, function(callback, results) {
callFunc("test", "data", callback)
},
function (err, results) {
console.log('===================================');
console.log('Async function');
})
var robject=[];
async.waterfall([
function (callback) {
for(var i in serial){
Router.find({},{r_serial_no:serial[i]},function (err,routerData) {
robject = robject.concat(routerData);
});
}
console.log('Robject= '+robject); //THIS RETURNS NULL
callback(null, robject);
},
function (blogs, callback) {
res.render('index', {dispatched_data:dispatched_data });
callback(null, 'Ended..' );
}
], function (err, result) {
console.log(result);
});
this is my waterfall model, here i need to access the robject from schema.find method to outside of that method. but it always return null..
how to access that??
You have the syntax error:
for(var i in serial){
Router.find({},{r_serial_no: i},function (err,routerData) {
robject = robject.concat(routerData);
});
}
the "for" loop defines "i" as next item in the array each iteration
The problem I see here is in for...in loop. Your callback will be fired even if your process i.e. Router.find is not completed. You can try below code, It might help.
Unlike your serial object please create a array called serials.
var robject=[];
async.waterfall([
function (callback) {
async.each(serials,
function(serial, localCb){
Router.find({},{r_serial_no:serial},function (err,routerData) {
robject = robject.concat(routerData);
localCb()
});
},
function(err){
console.log('Robject= '+robject);
callback(null, robject);
}
);
},
function (blogs, callback) {
res.render('index', {dispatched_data:dispatched_data });
callback(null, 'Ended..' );
}
], function (err, result) {
console.log(result);
});
I want to store all the data fetched from the database in arr_obj, then use this variable int async.forEachLimit function. For this reason i used async.series function, everything works fine except sleep(1000), code sleeps as soon as second function of async.series is called and then gives all the result together.Being a beginner in NodeJs i don't have much idea about all this.
var sleep = require('system-sleep');
//
//
var arr_obj = [];
async.series([
function (callback) {
Service.listAllUser(req.body, function (err, data) {
if(err) return callback(err);
arr_obj = data.toJSON();
callback();
});
},
function (callback1) {
console.log(arr_obj);
async.forEachLimit(arr_obj, 1, function (item, callback) {
Quality_Service.qualityService(item, function (err, data) {
if (err) return next(err);
console.log(data);
});
sleep(1000);
callback();
});
callback1();
}
], function (err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err);
res.send("okay");
});
Try to use callback inside the foreach, with setTimeout
async.forEachLimit(arr_obj, function (item, callback) {
Quality_Service.qualityService(item, function (err, data) {
if (err) return next(err);
setTimeout(callback, 1000, err);
});
});
I want to call two functions and get the results in parallel but one of the function's results needed to be adapted. So the function structure is:
function test(cb) {
async.parallel({
func1: function foo(cb1) {
cb(null, {});
},
func2: function bar(cb2) {
async.waterfall([
function adapt1(next) {
//do something;
},
function adapt2(next) {
//do something;
}
], function handler(err, res) {
//do something.
})
}
}, function handler2(err, res) {
cb(null, {});
})
}
However, it just seems hang there forever. not sure if I can use async in this way....
Sure you can! You have to be sure to call your callbacks in the correct order and in the first place. For example, func1 should be calling cb1 not cb. Secondly, your waterfall is not invoking their callbacks at all.
Take this code for example.
'use strict';
let async = require('async');
function test(callback) {
async.parallel({
func1: function(cb) {
cb(null, { foo: 'bar' });
},
func2: function(cb) {
async.waterfall([
function(cb2) {
cb2(null, 'a');
},
function(prev, cb2) {
cb2(null, 'b');
}
], function(err, result) {
cb(err, result);
});
}
}, function(err, results) {
callback(err, results);
});
}
test(function(err, result) {
console.log('callback:', err, result);
});
Outputs: callback: null { func1: { foo: 'bar' }, func2: 'b' }
How can I change my logic to retry if the err.retryable = true in the following code:
async.each(queues, function (queue, callback) {
sqs.getQueueUrl({'QueueName': queue.queue}, function (err, qurl) {
if (err) {
if (err.retryable) {
// How to retry sqs.getQueueUrl({'QueueName': queue.queue}...?
} else {
console.error(err, err.stack);
callback(err);
}
}else{
//Do lots of things here
}
})
}, function (err) {
//...
})
In addition to the advice by dfsq to name your callback and use it an asynchronously recursive manner, see also async.retry from the async module by Caolan McMahon. Example:
async.retry(3, apiMethod, function(err, result) {
// do something with the result
});
More complex example:
async.auto(
{
users: api.getUsers.bind(api),
payments: async.retry(3, api.getPayments.bind(api))
}, function(err, results) {
// do something with the results
}
);
More details in the docs.
UPDATE
A better solution for your use case:
I wrote a utility function that you can use to make your original method support any number of retries (with err.retryable support).
You can use it this way:
var retryingFunction = withRetries(sqs, sqs.getQueueUrl);
(Note that you need to provide both sqs and sqs.getQueueUrl)
And now you can use the retryingFunction just like you would use sqs.getQueueUrl but with a number of retries as the first arguments. The retries will only be done when err.retryable is true.
So now, instead of:
sqs.getQueueUrl({'QueueName': queue.queue}, function (err, qurl) {
// ...
});
you can use:
retryingFunction(3, {'QueueName': queue.queue}, function (err, qurl) {
// ...
});
where 3 is the number of retries.
And this is the function that I wrote to make the above possible:
function withRetries(obj, method) {
if (!method) {
method = obj;
obj = null;
}
if (typeof method != "function") {
throw "Bad arguments to function withRetries";
}
var retFunc = function() {
var args = Array.prototype.slice.call(arguments);
var retries = args.shift();
var callback = args.pop();
if (typeof retries != "number" || typeof callback != "function") {
throw "Bad arguments to function returned by withRetries";
}
var retryCallback = function (err, result) {
if (err && err.retryable && retries > 0) {
retries--;
method.apply(obj, args);
} else {
callback(err, result);
}
};
args.push(retryCallback);
method.apply(obj, args);
};
return retFunc;
}
See this LIVE DEMO to play with it and see how it works.
It works fine in the demo, I hope it will also work for your code.
You can give queue callback a name and provide it in retry request again. Try this:
async.each(queues, function (queue, callback) {
sqs.getQueueUrl({'QueueName': queue.queue}, function queueCallback(err, qurl) {
if (err) {
if (err.retryable) {
sqs.getQueueUrl({'QueueName': queue.queue}, queueCallback);
} else {
console.error(err, err.stack);
callback(err);
}
} else {
//Do lots of things here
}
});
}, function (err) {
//...
});