Newbie async.waterfall - node.js

I tried use async.
I have a rout function with a async.waterfall.
The 1st function call an external function and fetch all users in usersData
The 2nd function, via async.each, it call an external function to search infos for each user.
I want pass again usersData with the news values to the 3th function.
In the 3th function, for the moment, I have a async.each and I watch the datas for each user.
My issues
1) In the second function, I don't fetch the information for each user.
2) The 3th function is called before the 2nd and I don't fetch the new data
Thanks
router.post('/launch',function(req,res,next){
async.waterfall([
function(cb){
// fetch the global users
fetchUsers(usersData,cb);
},
function(usersData,cb){
async.each(usersData,
function(userdata,cb){
// fetch other data for each user
calcBalance(userdata, cb);
},function(err){
cb(err,usersData);
});
},
function(usersData,cb){
async.each(usersData,
function(userdata,cb) {
//watch the info with the news data
console.log(' 2 '+ JSON.stringify(userdata));
//console.log(3);
}
);
},
],
function(err,results){
console.log('Fin' + JSON.stringify(results));
res.render('synchros',{launch:'end'},results);
});
res.render('synchros',{launch:'end'});
});
function calcBalance(userData,cb){
var user_id=userData.id,
resultCalcBalance=0,
cats_id=[3,4,6],
tabData={};
async.each(cats_id,function(cat_id,cb){
switch (cat_id) {
case 3:
var comp = "<=";
break;
case 4:
var comp = "<=";
break;
case 6:
var comp = "<";
break;
}// fin du switch
var myquery = "select blabla+
//console.log(calcul_balance);
connectionMysql.query(myquery, function (err, rows, fields,cb) {
if (err) {
console.log('Error ' + err);
cb(err);
}
else if (rows.length != 0) {
if (rows != 0) {
}// end if
else {
}// end else
}); // end connectionMysql
},function(err){
cb(err,userData); // ?? I send the data here
});
cb(null, userData); // ?? I send the data here ??
}

I reindented, fixed some typos, and changed the names of the callbacks. I changed the second async.each to async.map because you're processing an array to get a set of one result per item.
The first problem was in the second to last line. You were calling back too early from calcBalance.
Another potential problem was an ambiguous callback name cb in the second waterfall function (as well as in calcBalance.)
Finally, you never ran the async.each callback in the third waterfall function, and if you calledback out of it, it was accidental.
You still aren't ever reporting success from one database query, so you will need to call done() if it worked. You might also want to use async.map for the database calls, this would let you assemble the results, like done(null, balanceForCategory)
router.post('/launch', function(req, res, next){
async.waterfall([
function(done){
// fetch the global users
fetchUsers(usersData,done);
},
function(usersData,done){
async.map(usersData, function(userdata, done2){
// fetch other data for each user
calcBalance(userdata, done2);
},function(err, results){
done(err,usersData);
});
},
function(usersData, done){
async.each(usersData, function(userdata, done2) {
//watch the info with the news data
console.log(' 2 '+ JSON.stringify(userdata));
//console.log(3);
}, done)
},
],
function(err, results){
// results will be undefined because we called done() from the above async.each
console.log('Fin' + JSON.stringify(results));
res.render('synchros', {launch:'end'}, results);
}); // end of async.each
}); // end of router.post()
function calcBalance(userData, callback){
var user_id=userData.id,
resultCalcBalance=0,
cats_id=[3,4,6],
tabData={};
async.each(cats_id, function(cat_id, done){
switch (cat_id) {
case 3:
var comp = "<=";
break;
case 4:
var comp = "<=";
break;
case 6:
var comp = "<";
break;
}// fin du switch
var myquery = "select blabla";
//console.log(calcul_balance);
connectionMysql.query(myquery, function (err, rows, fields, queryCb) { // what is this queryCb param?
if (err) {
console.log('Error ' + err);
queryCb(err); // This will callback whatever mySql passed in as queryCb
// done(err) // This will callback out of the async.each iterator and immediately the async.each callback
// callback(err) // This will callback out of calcBalance and continue executing
// return callback(err); // This will callback out of calcBalance and stop executing
} else if (rows.length != 0) {
if (rows != 0) {
// Your code might hang here without calling a callback
} else {
// Your code might hang here without calling a callback
}
}); // end connectionMysql
},function(err){
// Inside async.each callback. Either everything worked or something broke
callback(err,userData); // Send the data back out of calcBalance
});
//callback(null, userData); // Running this here will IMMEDIATELY call back before async.each runs
}

The calcBalance function
function calcBalance(userData,callback){
// Ensuite on va calculer les rtt_balances et holiday_balances et yesterday_extra_hours_month
var user_id=userData.id,
resultCalcBalance=0,
cats_id=[3,4,6],
tabData={},
dateJour=moment().format('YYYY-M-D');;
async.each(cats_id,function(cat_id,done){
switch (cat_id) {
case 3:
var comp = "<=";
break;
case 4:
var comp = "<=";
break;
case 6:
var comp = "<";
break;
}// fin du switch
var calcul_balance = "select * from table1"
connectionMysql.query(calcul_balance, function (err, rows, fields,queryCb) {
if (err) {
queryCb(err); // This will callback whatever mySql passed in as queryCb
// done(err) // This will callback out of the async.each iterator and immediately the async.each callback
// callback(err) // This will callback out of calcBalance and continue executing
// return callback(err); // This will callback out of calcBalance and stop executing
console.log('Error ' + err);
queryCb(err);
}
else if (rows.length != 0) {
if (rows != 0) {
// On va chercher les valuers sinon on les laisse à zéro par défaut.
for (var j = 0; j < rows.length; j++) {
if (!isNaN(rows[j].amount) && rows[j].amount != null) {
resultCalcBalance += parseInt(Math.round(rows[j].amount * 100) / 100);
//console.log('ResultCalculBalance 1chiffre ' + parseInt(Math.round(rows[j].amount*100)/100) + ' 2chiffre' + resultCalcBalance);
} else {
resultCalcBalance += 0;
//console.log('ResultCalculBalance 2' + JSON.stringify(rows[j].amount));
}
} // fin du for k
//console.log('Resultat : ' + userData.id + ' ' + cat_id + ' ' + resultCalcBalance);
if (cat_id == 3) userData.holiday_balance = resultCalcBalance;
if (cat_id == 4) userData.rtt_balance = resultCalcBalance;
if (cat_id == 6) userData.yesterday_extra_hours_month = resultCalcBalance;
}// fin du if
else {
if (cat_id == 3) userData.holiday_balance = 0;
if (cat_id == 4) userData.rtt_balance = 0;
if (cat_id == 6) userData.yesterday_extra_hours_month = 0;
}// fin du else
}// de la condition err ou pas
console.log('1 '+JSON.stringify(userData));
});
},function(err){
callback(err,userData);
});
//callback(null, userData); // Running this here will IMMEDIATELY call back before async.each runs

Related

memory leak in node.js app on AWS

I have some code in node, basically it is making api calls to external service and dump the returned data into a database. But it must have some serious memory leak since the node server will run out of memory in the middle. The AWS instance I am using is 2CPU, 4GB RAM. I spent a lot of time to figure out where is the leak with no luck yet. Below is the code, any hint will be helpful.
function refreshSitesBy5Min(rawData, callback){
var sites = JSON.parse(rawData).data;
if (typeof sites !== 'undefined' && sites.length > 0){
log.info('refreshing sites 5min');
sites.forEach(function(elem, index, array){
db.site_5min.find({siteid: elem.id, ts : moment(elem.ts).format('YYYY-MM-DDThh:mm:ss')}, function(err, found){
if (typeof found === 'undefined' || found == null || found.length == 0){
db.site_5min.save({
siteid : elem.id,
gran : '5min',
ts : moment(elem.ts).format('YYYY-MM-DDThh:mm:ss'),
wh_sum : elem.Wh_sum
}, function(err, inserted){
if (err){
log.error(err);
}
});
}
else{
db.site_5min.save({
id: found.id,
siteid : elem.id,
gran : '5min',
ts : moment(elem.ts).format('YYYY-MM-DDThh:mm:ss'),
wh_sum : elem.Wh_sum
}, function(err, updated){
if (err){
log.error(err);
}
})
}
})
})
}
else{
log.warn('no sites data');
}
callback();
}
and this is the code to call previous method:
function refreshSiteByGran(globalToken, gran, frequency){
log.info('refreshing site for ' + gran + ' table');
// db.site.find({}, function(err, sites){
db.run("select * from site", function(err, sites){
if (err){
log.error(err);
}
if (sites){
function handler(i){
if (i < sites.length){
var thePath = '/v3/sites/' + sites[i].siteid + '/data?fields=Wh_sum&tz=US/Pacific&gran=' + gran;
var end = moment().subtract(1, 'days').format('YYYY-MM-DDThh:mm:ss');
var start;
if (gran === '5min' || gran === 'hourly'){
start = moment(end).subtract(frequency, 'days').format('YYYY-MM-DDThh:mm:ss');
}
else if (gran === 'daily'){
start = moment(end).subtract(frequency, 'days').format('YYYY-MM-DDThh:mm:ss');
}
else if (gran === 'monthly'){
start = moment(end).subtract(frequency, 'months').format('YYYY-MM-DDThh:mm:ss');
}
thePath = thePath + '&start=' + start + '&end=' + end;
log.warn('thePath: ' + thePath);
var options = locusUtil.setOptions(thePath, globalToken.token.access_token);
request(options, function(err, result, body){
if (err){
log.error(err + ' path: ' + thePath);
}
if (body && JSON.parse(body).statusCode == 401){
getLocusToken(function(){
setTimeout(function(){
handler(i);
}, 2000);
})
}
else if (body && JSON.parse(body).statusCode == 200){
var data = JSON.parse(body).data;
// log.info('any data? ' + JSON.stringify(body, null, 4));
if (typeof data !== 'undefined' && data.length > 0){
if (gran === '5min'){
refreshSitesBy5Min(body, function(){
log.info('inserted: ' + data[0].id);
setTimeout(function(){
handler(i+1);
}, 2000);
})
}
if (gran === 'hourly'){
refreshSitesByHourly(body, function(){
log.info('inserted: ' + data[0].id);
setTimeout(function(){
handler(i+1);
}, 2000);
})
}
if (gran === 'daily'){
refreshSitesByDaily(body, function(){
log.info('inserted: ' + data[0].id);
setTimeout(function(){
handler(i+1);
}, 2000);
})
}
if (gran === 'monthly'){
refreshSitesByMonthly(body, function(){
log.info('inserted: ' + data[0].id);
setTimeout(function(){
handler(i+1);
}, 2000);
})
}
}
else{
setTimeout(function(){
handler(i+1);
}, 2000);
}
}
// re-try for concurrency error
else if (body && JSON.parse(body).statusCode == 429){
log.warn('error body ' + JSON.stringify(body));
setTimeout(function(){
handler(i);
}, 2000);
}
// if any other error, just skip
else {
setTimeout(function(){
handler(i+1);
}, 2000);
}
})
}
else{
return;
}
}
handler(0);
}
});
}
I believe the problem is inside this two blocks, I used memwatch to monitor v8 garbage collection, I see usage_trend is increasing fast, so it must have leaks.
This is very simple to solve...
First, get rid of the forEach loop, located here...
sites.forEach(function(elem, index, array){
Instead, create a recursive function that simply passes an index to the following iteration. What this does is create a loop that executes correctly in accordance to the given CPU and memory allotted. No need for process.nextTick() or any of that fancy jazz.
Asynchronous loops are not technically the answer, as they overload systems rather quickly with thousands of queues. Instead, iterate through each record, then only proceed to the next when the current process is finished.
Also, delete the current array index before proceeding to the next.
Eventually, the end of the loop is reached when the index returns "undefined". That is when the callback to the main function refreshSitesBy5Min is summoned.
function refreshSitesBy5Min(rawData, callback) {
var sites = JSON.parse(rawData).data
getSite(0)
function getSite(index) {
// we have reached the end
if(!sites[index])
return callback()
runProcess(sites[index]
// clear up memory after every iteration
delete sites[index]
// done with iteration, move on to the next
getSite(++index)
}
}
Still not done yet...
Big JSON Object
If your JSON object is massive, you will need to stream your JSON and handle tiny chunks at a time.
https://github.com/uhop/stream-json
Big Database Result Set
Your SQL query should utilize limit if you are returning more than 500 records results at a time, even smaller is better. So if your record set being returned is 100,000. Just grab 500 at a time in a recursive function, simply increment the index and multiply it by num_records, in this scenario: 500.
var offset = iter * 500
limit: [offset, 500]

node.js synchronous call gather results

I have a table where Persons are saved an their time (in seconds) where they were active.
I would like to write a function that gathers the total time in another table called gather.
For each row I am checking if an entry in the gather table exists. Depending on that result I make an insert or an update.
db.serialize(function() {
db.each("SELECT * from TEST", function(err, row) {
db.get("SELECT * from GATHER where name = " + row.name "", function(err, row) {
if(row === undefined || row === null){
var stmt = db.prepare("INSTER INTO gather (name, time) VALUE(?,?)");
stmt.run([name, seconds], function(error){
console.log('lastInsert ' + this.lastID);
});
stmt.finalize();
}else{
seconds += row.time;//increment time
var stmt = db.prepare("UPDATE gather SET time = ? WHERE name = ?");
stmt.run([seconds, row.name], function(error){
console.log('lastInsert ' + row.idProcessed);
});
stmt.finalize();
}
});
});
});
The problem that I ecounter is that sqlite runs asynchronously. Therefore multiple entries are created in my gather table although lines should be updated.
What would be the right way to run this function sychronously? Should I limit the lines and call the function every second or is there a smarted way?
You could use async. For example (but first you should read final notes at the end):
var async = require('async');
var data = {}
var yourFirstSelect() = function(callback){
//You do your first select
//...
db.get("SELECT * from TEST", function(err, row) {
if(row){
data.name = row.name;
data.otherInterestingAttribute = row.otherInterestingAttribute;
callback(err, data);
}else{
callback('Row is null');
}
})
//..
}
var yourSecondSelect() = function(data, callback){
//You do your second select
//...
db.get("SELECT * from GATHER where name = " + data.name "", function(err, row) { //Note that I'm using data instead of row
if(row){
data.seconds = row.seconds;
data.otherInterestingAttribute = row.otherInterestingAttribute;
callback(err, data);
}else{
callback('Row is null');
}
})
//..
}
var decide() = function(data, callback){
if (data.somethingExists) { //Do your validations
data.type = 'update';
callback(err, data);
} else {
data.type = 'insert';
callback(err, data);
}
}
var update() = function(data,callback){
if (data.type === 'update') {
//...
//Do your stuff in order to update
seconds += row.time;//increment time
var stmt = db.prepare("UPDATE gather SET time = ? WHERE name = ?");
stmt.run([seconds, row.name], function(error){
console.log('update ' + row.idProcessed);
});
stmt.finalize();
//...
} else {
callback(err,data);
}
}
var insert() = function(data,callback){
if (data.type === 'insert') {
//...
//Do your stuff in order to insert
var stmt = db.prepare("INSTER INTO gather (name, time) VALUE(?,?)");
stmt.run([data.name, data.seconds], function(error){
console.log('lastInsert ' + this.lastID);
callback(err,data);
});
stmt.finalize();
//...
} else {
callback(err,data);
}
}
var niceWorkflow = function(){
async.waterfall([
yourFirstSelect,
yourSecondSelect,
decide,
update,
insert
],
function (err, result) {
console.log('Done');
})
}
//and call your workflow
niceWorkflow();
Off course this is not a 100% working code, I wrote it in order you look another way to do what you are trying. Many variables, validations and more are just examples and I intentionally forgot the db.each to avoid being too extense and confusing you and trying to answer your final question Is there a smarter way?.
You can also use Q.all from Q library.
It will return a promise after all the promises are resolved. It one promise fails, then Q.all will be rejected.

NodeJS scope issue

var std;
......
...... // here I fill std with some data
......
I'm trying to access the 'std' array while handling this route:
app.post('/saveABS',function(req,res){
var fileName = "./public/"+f;
fs.writeFile(fileName, JSON.stringify(std, null, 4), function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved !");
console.log(a);
for(var i = 0; i < std.length; i++)
{
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'admin' ,
database : 'pfe'
});
console.log('before hr_cours = ' + std[i].hr_cours); // <========= WORKS
Here it displays the value of std[i].hr_cours correctly.
if(std[i].hr_cours != a[i].hr_cours)
{
connection.connect(function(error){
console.log('after hr_cours = ' + std[i].hr_cours); // <============ undefined
Here it displays undefined.
if(error)
{
console.log('Unable to connect to database.');
}
else
{
console.log("i= " + i);
console.log("I'm in : hr_cours = " + std[i].hr_cours);
connection.query("INSERT INTO assiste VALUES (CURDATE(),(SELECT id_etud FROM Etudiant WHERE nom_etud = ?),(SELECT s.id_seanceFROM Seance s, aura_lieu a, jour j, fait_objet fWHERE s.id_seance = a.id_seanceAND CURTIME() BETWEEN a.heure_debut AND a.heure_fin AND a.id_jour = j.id_jour AND j.dat = CURDATE() AND f.id_seance = s.id_seance AND f.id_ens = ?), ?)", [std[i].nom_etud, idEns, std[i].hr_cours], function(er1, res1){
if(er1)
{
console.log('Query error: ');
}
else
{
console.log('success');
}
});
}
});
}
}
res.end("all");
}
});
});
I noticed that the issue is triggered once the execution flow enters the connection.connect block and that it's a scope issue; I searched everywhere for the answer but couldn't find a solution to my specific problem.
How can I make this work?
The execution of connection.connect is delayed and therefore when it fires i is not the same value as when a reference to i was passed in to it by the for loop. So, to lock in the value of i in that particular loop execution, wrap the connection.connect method and callback in an IIFE and pass i as it's parameter:
~function(i) {
connection.connect(function(error){
console.log('after hr_cours = ' + std[i].hr_cours);
// other stuff
})
}(i)
A more detailed explanation of IIFE's and scope closure in my answer here: Odd javascript code execution order
And a JSFiddle playground to explore the technique: IIFE Scope Closure With Pre-Existing Parameters.
Here's your problem:
app.post('/saveABS', function(req, res) {
// ...
for (var i = 0; i < std.length; i++) {
// ...
// This is SYNC. Here i = 0,1,2...
connection.connect(function(error) {
// This is A-SYNC.
// Here i = std.length + 1
// always!
// No matter even if it syntactically looks like i should be 1,2,3 according to the loop
// but all of this is executed way after the loop has long been completed
// ...
// Here it displays undefined.
console.log('after hr_cours = ' + std[i].hr_cours); // <============ undefined
A solution: Have a self executing anonymous function (function(){})() inside the loop and pass it the arguments (i) so that they will stay the same
for (var i = 0; i < std.length; i++) (function(i){
connection.connect(function(error) {
// ...
// Here, since i is an argument passed to outer function
// i will be i,2,3...
})(i);
Another solution: forEach loop, which already has a callback function
std.forEach(function(currentValue, i, std){
connection.connect(function(error) {
// ...
// Same here, since i is an argument passed to outer function
// i will be i,2,3...
// Also currentValue = std[i] so you can use that
});

Function returning undefined Node JS

I am currently trying to iterate through an array of JSON elements, parse and add the data I need into a specially formatted string, and once conditions are met, initiate the uploading of this data.
The problem that I am running into, however, is my variable 'deviceOutString' is being returned as undefined, leaving me with a string of 'undefined' written as many time as there are JSON elements in the array. I know that the return from the 'checkDuplicates' function is correct because before returning the value, the logs show that the value is correct.
I have attached my code below, please let me know if you have any ideas.
Thanks!
Old Code (updated below)
var i=0;
var parsedJson = JSON.parse(storedData) ;
var storedDataSize = parsedJson.length;
console.log('Stored Data Size: '+storedDataSize);
var async = require('async');
async.each(parsedJson, function( subElemJson, callback1) {
async.series([
function(callback){
console.log('dstring: ' + deviceOutString);
console.log('i : ' + i);
var subElemJsonPretty = JSON.stringify(subElemJson,null,0) ;
var date = subElemJson['date'];
deviceOutString += checkDuplicates(subElemJson, deviceOutString);
console.log('theLoop*DString: ' + deviceOutString);
callback(null, 'one');
},
function(callback){
if((i == storedDataSize - 1 || count == 225) && storedDataSize > 0) {
writeDCC(deviceOutString);
count = 0;
makeList();
}
i++;
callback(null, 'two');
setTimeout(function () { callback1(); }, 500);
}
]);
}); }
Updated New Code
function theLoop(storedData) {
var deviceOutString = '<list>';
var temp;
try {
var i=0;
var parsedJson = JSON.parse(storedData) ;
var storedDataSize = parsedJson.length;
console.log('Stored Data Size: '+storedDataSize);
var async = require('async');
var delayed = require('delayed');
async.each(parsedJson, function( subElemJson, callback1) {
async.series([
function(callback){
var subElemJsonPretty = JSON.stringify(subElemJson,null,0) ;
var date = subElemJson.date;
console.log('THIS IS THE DATE: '+date);
temp = checkDuplicates(subElemJson, deviceOutString);
console.log('This is the temp: ' + temp);
callback(null, temp);
}
], function(results){
console.log('*****Results are In*****: ' + results);
deviceOutString =+ temp;
if((i == storedDataSize - 1 || count == 225) && storedDataSize > 0) {
writeDCC(deviceOutString);
count = 0;
deviceOutString = '<list>';
}
i++;
callback1(results);
});
},
function(err){
if( err ) {
console.log('A file failed to process');
} else {
console.log('All files have been processed successfully');
}
});
} catch (error) {
console.info('Exception parsing ' + '\n\n' + error);
return;
}
}
So a few things
1: var date = subElemJson['date']; accessing object properties via array syntax is a bad practice. Nit picky but hey :P try var data = subElemJson.date; instead.
2: deviceOutString isn't defined anywhere in the code you provided.
3: Both async.series and async.each are going to want a callback function for when each is finished. that's the whole point of calling callback(null, 'one'); -- that you pass a value to the "results" array in the final async.series callback. You are calling setTimeout(function() { callback1(); }, 500); in the wrong place (also arbitrarily putting it behind a timeout?).
The proper async.series formatting is thus:
async.series([
function(callback) {
// do stuff
callback(null, someValue);
},
function(callback) {
// do other stuff
callback(null, someOtherValue);
}
], function(results) {
// all the stuffs are done
console.log(results); <-- results is an array containing "someValue" and "someOtherValue" from the iterations above
callback1(results);
});
Also, async.each is in the same boat -- it expects you to pass a "every thing I'm looping through has completed now!" function at the end.
Async docs on .each() (scroll down for docs on .series()): https://github.com/caolan/async#each

How to send mongodb data async from inner functions to outside callback function from a for loop?

NEW POST:
Here is the sample of the working async code without a db.
The problem is, if i replace the vars (data1_nodb,...) with the db.collection.find();
function, all needed db vars received at the end and the for() loop ends not
correct. I hope that explains my problem a bit better. OA
var calc = new Array();
function mach1(callback){
error_buy = 0;
// some vars
for(var x_c99 = 0; x_c99 < array_temp_check0.length;x_c99++){
// some vars
calc[x_c99] = new Array();
calc[x_c99][0]= new Array();
calc[x_c99][0][0] = "dummy1";
calc[x_c99][0][1] = "dummy2";
calc[x_c99][0][2] = "dummy3";
calc[x_c99][0][3] = "dummy4";
calc[x_c99][0][4] = "dummy5";
function start_query(callback) {
data1_nodb = "data1";
data2_nodb = "data2";
data3_nodb = "data3";
data4_nodb = "data4";
calc[x_c99][0][0] = data1_nodb;
calc[x_c99][0][1] = data2_nodb;
calc[x_c99][0][2] = data3_nodb;
callback(data1_nodb,data2_nodb,etc..);
}
start_query(function() {
console.log("start_query OK!");
function start_query2(callback) {
data4_nodb = "data5";
data5_nodb = "data6";
data6_nodb = "data7";
calc[x_c99][0][3] = data4_nodb;
calc[x_c99][0][4] = data5_nodb;
callback(data5_nodb,data6_nodb,etc..);
}
start_query2(function() {
console.log("start_query2 OK!");
function start_query3(callback) {
for(...){
// do something
}
callback(vars...);
}
start_query3(function() {
console.log("start_query3 OK!");
});
});
});
}
callback(calc);
};
function mach2(callback){
mach1(function() {
console.log("mach1 OK!");
for(...){
// do something
}
});
callback(calc,error_buy);
};
mach2(function() {
console.log("mach2 OK 2!");
});
OLD POST:
i try to read data from the mongodb and send them back with a callback to the next
function, that needs the infos from the db to proceed.
Without the mongodb read functions it works perfect but now i dont know how
i can send the db vars out of the two inner functions to the first callback function.
Hope someone can help me...
Thanks
var error = 0; var var1 = "yessir";
function start_query(callback) {
var db_name = "db1";
db[db_name].find({name:var1},{data1:1, data2:1, data3:1, data4:1}, function(err, data_catch,callback) {
if( err || !data_catch ) {
console.log("Problem finding data_catch:" + err);
} else {
data_catch.forEach( function(data_catch_finder,callback) {
data1_db = data_catch_finder.data1;
data2_db = data_catch_finder.data2;
data3_db = data_catch_finder.data3;
data4_db = data_catch_finder.data4;
if(data1_db == "" || data2_db == "" || data3_db == "" || data4_db == ""){error = 1; console.log("Error: data_catch_finder");}
callback(data1_db, data2_db, data3_db, data4_db, error);
});
}
});
callback(data1, data2, data3, data4, error);
}
//########################################################################
start_query(function() {
function start_query2(callback) {
console.log("DATA1 from callback:" + data1_db);
console.log("DATA2 from callback:" + data2_db);
console.log("DATA3 from callback:" + data3_db);
console.log("DATA4 from callback:" + data4_db);
var var_no_db = "testing";
//do something else and callback
callback(var_no_db);
}
start_query2(function() {
console.log("Var from callback start_query2:" + var_no_db);
console.log("The end");
});
});
your callback signature are issuing callback as a parameter.
As far as I can understand your code, you need to keep reference of the first callback, the one you receive here: function start_query(callback).
In every callback function you made the mistake to bind the variable name callback to the parameter from the mongo driver (a simple undefined i think).
You can fix it removing every reference of callback from the signature of your inner functions.
a simple example:
function async (cb) {
// params: Error, data
cb(null, 'moo');
}
function foo(callback) {
async(function(err, data, callback){
console.log(callback); // undefined
});
console.log(callback); // Moo
}
foo('Moo');
Take a look at Eloquent Javascript to better understand the JS context switching;
edit
The only way to wait the results of an async function is recall the first callback inside the last nested callback.
function ugly_nested (callback) {
dbquery('...', function(err, data_01) {
if (!! err) return callback(err);
dbquery('...', function(err, data_02) {
if (!! err) return callback(err);
dbquery('...', function(err, data_03) {
if (!! err) return callback(err);
callback(null, data_01, data_02, data_03);
});
});
});
}
ugly_nested(function(err, data01, data02, data03) {
if (!! err) throw err;
manage_data(data01, data02, data03);
});
The FOR loop is synchronous, but, the database calls are asynchronous, so, the for loop will end before the database returns his results. If you really need that for loop you can try out one of the nice flow control libraries out there

Resources