I try to use async NodeJs library to execute a render with express after collecting informations of a API but the async.parallel callback execute itself before collecting all data that I need
This is the code :
LolApi.getMatchHistory(app_dtb.summoner.id, "euw", function(err, history) {
if (!err) {
async.parallel([
function(callback) {
LolApi.getMatch(history.matches[nbMatch].matchId, function(err, match) {
if (!err) {
var teamIn;
function getParticipantNb() {
for (var i = 0; i < match.participantIdentities.length; i++) {
if (app_dtb.summoner.id == match.participantIdentities[i].player.summonerId) {
if (i <= 5) teamIn = 100
else teamIn = 200
return i + 1
}
}
return false;
}
var participantNb = getParticipantNb()
if (match.teams[0].winner == true && teamIn == 100) {
app_dtb.lastGame.won = "Win";
} else {
app_dtb.lastGame.won = "Loose";
}
console.log(app_dtb.lastGame.won)
} else {
console.log(err)
}
});
setTimeout(function() {
callback(null, "one");
}, 200);
},
function(callback) {
options = {
champData: 'allytips,blurb',
version: '4.4.3',
locale: 'en_US'
}
LolApi.Static.getChampionById(history.matches[nbMatch].champion, options, function(err, champ) {
if (!err) {
console.log(champ.name);
app_dtb.lastGame.champName = champ.name
} else {
console.log(err)
}
});
setTimeout(function() {
callback(null, "two");
}, 100);
}
], function(err, results) {
console.log(results)
res.render("index");
});
} else {
console.log(err);
}
})
Any idea or other way to have the same result ?
Thanks a lot
You should call the callback inside your LolApi methods callback and be sour that the async callback will be called for two parallel functions eventually. so may the timeout called before your LolApi callbacks.
Related
I'm working on a nodeJS script and I would like to know how to execute a function after an another one.
Because actually i need to save in my database some data and then retrieve them. However for the moment my retrieve is executed before my save :/
Have already looked on internet there is a lot of example I tried them but for the moment no one worked ... I should probably do something wrong if somebody could help me on it :)
function persistMAP(jsonData, callback) {
console.log(jsonData);
//Deck persistance
for (var i = 0; i < 1; i++) {
(function(i) {
var rowData = new DeckDatabase({
_id: new mongoose.Types.ObjectId(),
DeckNumber: Number(jsonData.Deck[i].DeckNumber),
x: Number(jsonData.Deck[i].x),
y: Number(jsonData.Deck[i].y),
});
rowData.save(function(err) {
if (err) return console.log(err);
for (var i = 0; j = jsonData.Units.length, i < j; i++) {
(function(i) {
var unit = new MapDatabase({
//UnitID: mongoose.ObjectId(jsonData.Units[i].UnitID),
UnitID: jsonData.Units[i].UnitID,
TypeID: Number(jsonData.Units[i].TypeID),
x: Number(jsonData.Units[i].x),
y: Number(jsonData.Units[i].y),
_id: mongoose.Types.ObjectId(jsonData.Units[i].Code + 'dd40c86762e0fb12000003'),
MainClass: jsonData.Units[i].MainClass,
Orientation: jsonData.Units[i].Orientation,
Postion: jsonData.Units[i].Postion,
Deck: String(rowData._id)
});
unit.save(function(err) {
if (err) return console.log(err);
console.log('save');
});
})(i);
}
});
})(i);
}
callback();
};
app.get("/Map", function(req, res) {
console.log("got");
var urlTempBox = 'http://localhost:3000/MapCreate';
DeckDatabase.find(null, function(err, data) {
if (err) {
throw (err);
}
if (data.length != 0) {
MapDatabase.find()
.populate('Deck')
.exec(function(err, finalData) {
res.send(finalData);
});
} else {
request(urlTempBox, data, function(error, response, body) {
if (error) {
throw (error);
} else {
var jobj = JSON.parse(response.body);
console.log("persist begin");
persistMAP(jobj, function() {
console.log('retrieve Done');
});
}
});
}
});
You can use javascript callbacks like this:
User.findById(user_id,function(err,data){
//Inside this you can call another function.
});
console.log("Hello"); //this statement won't wait for the above statement
The more good approach would be use promises.
You can also use async await function to handle asynchronus tasks.
Async Await Style
async function getData(){
let user_data=await User.findById(user_id);
let user_videos=await Videos.findById(user_data._id); //user_data._id is coming from the above statement
}
But you can only use await in async methods.
Hope it helps.
I am working in node js express framework and I have a scenario where I have to call 2-3 nested callback functions inside for loop.
Below is my code:
for (i in jdp_tb_trades) {
var jdp_response_json_parsed = JSON.parse(jdp_tb_trades[i].jdp_response);
processConsign(jdp_tb_trades[i].tid, function(err_process_consign, lpnumber) {
if (err_process_consign) {
console.log("Some error occurred in processConsign. Error is:" + err_process_consign);
//Check if generate XML is enabled from admin end.
configuration.getOneByKey('generateXml', function(err_configuration, result_configuration) {
if (err_configuration) {
console.log('[generateXml]: generate xml enabled/disabled - No response.');
return callback(null, lpnumber);
} else {
if (result_configuration.value == 'true') {
console.log('[generateXml]: generate xml enabled.')
generateXml(jdp_tb_trades[i].tid, jdp_response_json_parsed, lpnumber, function(err_generate_xml, success_generate_xml);
if (err_generate_xml) {
return callback(err_generate_xml);
} else {
return callback(null, success_generate_xml);
}
});
} else {
console.log('[generateXml]: generate xml disabled.');
return callback(null, lpnumber);
}
}
});
} else {
//Check if generate XML is enabled.
configuration.getOneByKey(
'generateXml',
function(err_configuration, result_configuration) {
if (err_configuration) {
console.log('[generateXml]: generate xml enabled/disabled - No response.');
return callback(null, lpnumber);
} else {
if (result_configuration.value == 'true') {
console.log('[generateXml]: generate xml enabled.')
generateXml(jdp_tb_trades[i].tid, jdp_response_json_parsed, lpnumber, function(err_generate_xml, success_generate_xml) {
if (err_generate_xml) {
return callback(err_generate_xml);
} else {
return callback(null, success_generate_xml);
}
});
} else {
console.log('[generateXml]: generate xml disabled.');
return callback(null, lpnumber);
}
}
});
});
}
Update
The above code is part of a function named getOrders which is called as:
module.exports = {
getOrders: function (callback) {
getOrders(function(err_getOrders, getOrdersResponse){
if(err_getOrders){
console.log("generate Order process error:"+err_getOrders);
return callback(err_getOrders);
}
else{
console.log("generate Order process success:"+getOrdersResponse);
return callback(null, getOrdersResponse);
}
});
},
}
I have made multiple callbacks because function ends in multiple scenarios. I am not concerned about output of getOrders because I am not going to consume that output anywhere.
Here I have two functions processConsign and generateXml. generateXml is called in callback of processConsign. But I think forloop does not wait for these two tasks to get complete and keep increment the loop without waiting for processing of these two functions.
Is there any way by which I can make for loop wait for completion of these two processes and then executing the next loop?
you can use the async.each
async.each(jdp_tb_trades, (jdp_tb_trade, callback) => {
// do manipulation here
// return callback() after the process. pass err if error
}, loop_ended (err) => {
if (err) {
// Error in loop | err callback returned with err
}
// loop already ended here
});
Kindly check this
const async = require('async');
function getOrders (callback) {
async.each(jdp_tb_trades, generate_xml, (err) => {
if (err) {
// the callback return err using callback(err)
}
else {
// check the jdp_tb_trades. no error found
}
});
}
function generate_xml (jdp_tb_trade, callback) {
let jdp_response_json_parsed;
try {
jdp_response_json_parsed = JSON.parse(jdp_tb_trade.jdp_response);
} catch (err) {
return callback(err);
}
processConsign(jdp_tb_trade.tid, (err_process_consign, lpnumber) => {
if (err_process_consign) {
console.log(`Some error occurred in processConsign. Error is: ${err_process_consign}`);
//Check if generate XML is enabled from admin end.
configuration.getOneByKey('generateXml', (err_configuration, result_configuration) => {
if (err_configuration) {
console.log('[generateXml]: generate xml enabled/disabled - No response.');
// base on your callback it still a success response
// return callback(null, lpnumber);
jdp_tb_trade.lpnumber = lpnumber;
return callback();
}
else {
if (result_configuration.value == 'true') {
console.log('[generateXml]: generate xml enabled.')
generateXml(jdp_tb_trade.tid, jdp_response_json_parsed, lpnumber, (err_generate_xml, success_generate_xml) => {
if (err_generate_xml) {
jdp_tb_trade.err_generate_xml = err_generate_xml;
// return error
return callback(err_generate_xml);
} else {
jdp_tb_trade.success_generate_xml = success_generate_xml;
return callback();
// return callback(null, success_generate_xml);
}
});
}
else {
console.log('[generateXml]: generate xml disabled.');
return callback(null, lpnumber);
}
}
});
}
else {
if (result_configuration.value == 'true') {
console.log('[generateXml]: generate xml enabled.')
generateXml(jdp_tb_trades[i].tid, jdp_response_json_parsed, lpnumber, (err_generate_xml, success_generate_xml) => {
if (err_generate_xml) {
return callback(err_generate_xml);
} else {
jdp_tb_trade.success_generate_xml = success_generate_xml;
// return callback(null, success_generate_xml);
return callback();
}
});
}
else {
console.log('[generateXml]: generate xml disabled.');
jdp_tb_trade.lpnumber = lpnumber;
return callback();
// return callback(null, lpnumber);
}
}
});
}
var data = [10,21,33,40,50,69];
var i = 0;
var dataSeq = [];
while(i<data.length){
if(data[i]%2 == 0){
store.findOne({'visibility': true},function(err, data){
dataSeq.push(i)
i++;
});
}
else{
dataSeq.push(i)
i++;
}
}
if(i==data.length-1){
console.log(dataSeq) // Should Print [1,2,3,4,5]
return res.status(200).send({ message: 'Task Completed'})
}
I want to collect data as per loop excecutes.
I am aware about how to handle async calls in nodejs. But I want the callbacks in sequence.
e.g. Though there is a async call in if condition i want to hault the loop, so that I can push value of i in dataSeq and it will result in [1,2,3,4,5] array. I want that sequence because my post operations are dependent on that sequence.
I think asyncjs#eachSeries has what you need.
Your code would become something like this:
async.each(data, (item, callback) => {
if(item%2 == 0){
store.findOne({'visibility': true},function(err, data){
dataSeq.push(i)
i++;
});
}
else{
dataSeq.push(i)
i++;
}
}, (err) => {
// if any of the callbacks produced an error, err would equal that error
});
You can use something like async#eachOf
var async = require('async');
var data = [10,21,33,40,50,69];
var dataSeq = [];
async.eachOf(data, function(value, key, cb) {
if (value % 2 == 0) {
store.findOne({ 'visibility': true })
.then(function(doc) {
dataSeq.push(key);
})
.catch(function(err) {
return cb(err);
});
} else {
cb();
}
}, function(err) {
if (err) {
console.error(err)
return res.status(500).send(); # handle the error as you want
}
return res.status(200).send({ message: 'Task Completed'})
})
This is a function for manipulating some images via graphicsmagick in my meteor application.
If the image is being cropped, I want to check if its width is >900px. Then it should be resized to 900px.
This complete function is not working, as the callback of gmread.size() has to be finished before doing the return gmread.stream - which is currently not the case. But I don't know how to get this synchron/asynchron thing working...
function imageManipulation(inputId, method) {
var inStream = Files.findOneStream({ _id: inputId }),
gmread;
// do some image manipulation depending on given `method`
if (method == 'crop') {
gmread = gm(inStream);
// check if image width > 900px, then do resize
gmread.size(function(err, size) {
if (size.width > 900) {
gmread = gmread.resize('900');
}
});
}
return gmread.stream('jpg', Meteor.bindEnvironment(function(err, stdout, stderr) {
stderr.pipe(process.stderr);
if (!err) {
var outStream = Files.upsertStream({ _id: outputFileId }, {}, function(err, file) {
if (err) { console.warn("" + err); }
return;
});
return stdout.pipe(outStream);
}
}));
});
Update
I tried to use meteorhacks:async, but I do get the error Exception in callback of async function: TypeError: Object [object Object] has no method 'size'.
function imageManipulation(inputId, method) {
var inStream = Files.findOneStream({ _id: inputId }),
gmread;
if (method == 'crop') {
gmread = gm(inStream);
// Asnyc doesn't work
gmread = Async.runSync(function(done) {
gmread.size(function(err, size) {
if (size.width > 900) {
gmread = gmread.resize('900');
}
done(null, gmread);
});
});
}
return gmread.stream('jpg', Meteor.bindEnvironment(function(err, stdout, stderr) {
stderr.pipe(process.stderr);
if (!err) {
var outStream = Files.upsertStream({ _id: outputFileId }, {}, function(err, file) {
if (err) { console.warn("" + err); }
return;
});
return stdout.pipe(outStream);
}
}));
});
you can use meteorhacks:async that pause the execution until you invoke done() callback as shown below.
function imageManipulation(inputId, method) {
var inStream = Files.findOneStream({ _id: inputId }),
gmread;
// do some image manipulation depending on given `method`
if (method == 'crop') {
gmread = gm(inStream);
// check if image width > 900px, then do resize
gmread.size(function(err, size) {
if (size.width > 900) {
gmread = gmread.resize('900');
}
});
}
var response = Async.runSync(function(done) {
gmread.stream('jpg', Meteor.bindEnvironment(function(err, stdout, stderr) {
stderr.pipe(process.stderr);
if (!err) {
var outStream = Files.upsertStream({ _id: outputFileId }, {}, function(err, file) {
if (err) { console.warn("" + err); }
return;
});
done(null, stdout.pipe(outStream));
}
}));
});
return response.result
});
waterfall function with two calls but the second on is not waiting for the first one to completely finish. The first one has a mongodb.find() call in it.
Here is the async-waterfall function
app.get("/news", function(req, res) {
async.waterfall([
function (callback) {
var blogs = tendigiEngine.getAllBlogs(callback);
callback(null, blogs);
},
function (blogs, callback) {
var array = tendigiEngine.seperateBlogs(blogs, callback);
callback(null, array );
}
], function (err, result) {
// result now equals 'done'
console.log("done");
console.log(result);
});
});
Here are the two functions being called:
getAllBlogs():
exports.getAllBlogs = function() {
Blog.find(function(err, theBlogs){
if(!err) {
return theBlogs;
}
else {
throw err;
}
});
}
seperateBlogs():
exports.seperateBlogs = function(blogs) {
if(blogs.length === 0 ) {
return 0;
}
else {
blogs.reverse();
var blog = blogs[0];
blogs.shift();
var finArray = [blog, blogs];
return finArray;
}
console.log("asdf");
}
It is important that seperateBlogs won't be called before getAllBlogs() has returned theBlogs, but it is being called before the value is returned. I used Async_Waterfall to avoid this problem but it keeps recurring, which means I am using it wrong. What am I doing wrong here and how can I fix it?
Thanks!
Your exported functions are missing the callback parameters:
exports.getAllBlogs = function(cb) {
Blog.find(cb);
};
exports.seperateBlogs = function(blogs, cb) {
if (blogs.length === 0 )
return cb(null, blogs);
blogs.reverse();
var blog = blogs[0];
blogs.shift();
cb(null, [blog, blogs]);
}
Then your main code can be simplified as well:
async.waterfall([
tendigiEngine.getAllBlogs,
tendigiEngine.seperateBlogs
], function (err, result) {
// result now equals 'done'
console.log("done");
console.log(result);
});