Looping an array with async module - node.js

I have to do a query to the DB and then for each element in the result array I have to do another query, and then complete the code. I'm using async.waterfall to do the tasks and inside one function I use async.forEachSeries to do the looping.
async.waterfall([
function(callback){
connection.query(sql, params, function(err, rows, fields){
if(err) throw err;
callback(null, rows);
})
},
function(rows, callback){
var result = [];
var aux = {};
for(var i in rows){
if(typeof aux[rows[i].id] == 'undefined'){
var obj = {
'id': rows[i].id,
'title': rows[i].title,
'type': rows[i].type,
'url': rows[i].url,
'last_update': rows[i].last_update,
'unixend': rows[i].unixend,
'client_id': rows[i].id_client,
'client_name': rows[i].client_name,
'background_timeline': rows[i].background_timeline,
'description': rows[i].description,
'init': rows[i].init,
'end': rows[i].end,
'days': rows[i].days,
'timeinit': rows[i].timeinit,
'timeend': rows[i].timeend,
'lat': rows[i].lat,
'lon': rows[i].lon,
'url2': rows[i].url2,
//'locations': [ ],
};
aux[rows[i].id] = obj;
}
}
callback(null, aux, result)
},
function(aux, result, callback){
async.eachSeries(aux,
function(obj, callback){
connection.query(sql2, [obj['id'], obj['id'], obj['id']], function(err, data){
obj['locations'] = data;
callback();
})
},
function(err){
if(err) throw err;
else callback(null, aux, result)
});
},
function(aux, result, callback){
for(var i in aux){
result.push(aux[i]);
}
callback(null, result);
}
],
function(err, result){
if(err) callback1(err, []);
else callback1(false, result);
}
);
Here, callback1 is the callback passed as an argument to the global function when it's called. Also, sql and sql2 are the queries stored in variables.
The problem is that it never enters at the eachSeries method. Why is that?

Related

Node Async.auto Callback is not a function

I've got a problem with my nodejs script and async library.
Ive got an async.auto with 2 parts. First part ok, in second part I've got another async.auto with 2 parts which are nearly the same, some changes in sql queries and arrays. But the first part here works fine, the second one the zutKopf stops with failure that the callback function cbzutk is not a function. Ive tried to fixed it somedays but can't find out the failure because its written like the first part zubKopf.
Hopefully u can help me.
Greets
async.auto({
one: function(cbone){
db.getConnection(function(err, dbs){
dbs.query("insert into rezepte (kat_id, rez_name, rez_desc, rez_img) values(?,?,?,?)",[RezeptComp.rezept.kat_id,RezeptComp.rezept.rez_name,RezeptComp.rezept.rez_desc,RezeptComp.rezept.rez_img],function(err, rows) {
if (err) {
cbone(err);
return;
}
console.log('query Rezept');
rezeptId = rows.insertId;
RezeptComp.rezept.rez_id = rows.insertId;
dbs.release();
cbone(null);
});
});
},
two: ['one', function(cbtwo){
async.auto({
zubKopf: function(cbzubk){
async.eachSeries(RezeptComp.zubereitung, function(data,next){
db.getConnection(function(err,dbzubk){
dbzubk.query("insert into zubereitungKopf (rez_id, zubk_text) values("+ rezeptId +",'"+ data.zubk_text +"')",function(err, rows) {
if (err) {
cbtwo(err);
return;
}
console.log('query zubk');
zubkId = rows.insertId;
RezeptComp.zubereitung[zubkd].zubk_id = zubkId;
dbzubk.release();
async.eachSeries(data.zubp, function(data2,next2){
db.getConnection(function(err,dbzubp){
dbzubp.query("insert into zubereitungPos (zubk_id, zubp_text) values("+ zubkId +",'"+ data2.zubp_text +"')",function(err, rows) {
if (err) {
cbtwo(err);
return;
}
console.log('query zubp');
zubpId = rows.insertId;
RezeptComp.zubereitung[zubkd].zubp[zubpd].zubp_id = zubpId;
dbzubp.release();
zubpd = zubpd + 1;
next2(null);
});
});
}, function(err, results){
zubkd = zubkd + 1;
zubpd = 0;
next(null);
});
});
});
}, function(err, results){
cbzubk(null);
});
},
zutKopf: ['zubKopf', function(cbzutk){
async.eachSeries(RezeptComp.zutaten, function(data3,next3){
db.getConnection(function(err,dbzutk){
dbzutk.query("insert into zutatenKopf (rez_id, zutk_text) values("+ rezeptId +",'"+ data3.zutk_text +"')",function(err, rows) {
if (err) {
cbzutk(err);
return;
}
console.log('query zutk');
zutkId = rows.insertId;
RezeptComp.zutaten[zutkd].zutk_id = zutkId;
dbzutk.release();
async.eachSeries(data3.zutp, function(data4,next4){
db.getConnection(function(err,dbzutp){
dbzutp.query("insert into zutatenPos (zutk_id, zutp_text) values("+ zutkId +",'"+ data4.zutp_text +"')",function(err, rows) {
if (err) {
cbzutk(err);
return;
}
console.log('query zutp');
zutpId = rows.insertId;
RezeptComp.zutaten[zutkd].zutp[zutpd].zutp_id = zutpId;
dbzutp.release();
zutpd = zutpd + 1;
next4(null);
});
});
}, function(err, results){
zutkd = zutkd + 1;
zutpd = 0;
next3(null);
});
});
});
}, function(err, results){
cbzutk(null);
});
}],
final: ['zubKopf', 'zutKopf', function(error, results){
cbtwo(null);
}]
});
}],
final: ['one', 'two', function(error, results){
callback(RezepComp);
}]
});
In async.auto depending functions receive as first a parameter from the previous function and then you can define the callback in the functionheader like two: ['one', function(parameter, callback) instead of two: ['one', function(callback).

async waterfall callback was already called error in nodejs

I am newbee to nodejs. I have been scratching my head for long time on why below code throwing error.I found some similar in stack overflow, but couldn't get help in finding the bug.
var albums_coll,photos_coll;
async.waterfall([
function(cb){
MongoClient.connect(url,
(err,dbase)=>{
if(err) {
console.log('bad');
process.exit(-1);
}
console.log("I have a connection!");
db=dbase;
cb(null);
}
);
},
function(cb){
db.collection("albums",cb);
},
function(album_obj,cb){
albums_coll = album_obj;
db.collection("photos",cb);
},
function(photos_obj,cb){
photos_coll = photos_obj;
cb(null);
},
function(cb){
albums_coll.InsertOne(a1,cb);
},
function(inserted_doc,cb){
console.log("I have Inserted a document!!");
console.log(inserted_doc);
cb(null);
}
],
function(err,results){
console.log("Done!!!");
console.log(err);
console.log(results);
db.close();
});
Please suggest!!
the following is the error its showing.
I have a connection!
C:\Users\thathine\NodeLive\Chapter08\mongotest\node_modules\mongodb\lib\mongo_client.js:433
throw err
^
Error: Callback was already called.
at C:\Users\thathine\NodeLive\Chapter08\mongotest\node_modules\async\dist\async.js:903:32
at Db.collection (C:\Users\thathine\NodeLive\Chapter08\mongotest\node_modules\mongodb\lib\db.js:466:27)
.
.
.
The callback() function you are passing into the db.collection() function is being called twice. The first time this happens is inside the MongoDB library while invoking db.collection(). It is happening in the following block - I added a comment pointing to line 466 as shown in the stack:
if(options == null || !options.strict) {
try {
var collection = new Collection(this, this.s.topology, this.s.databaseName, name, this.s.pkFactory, options);
if(callback) callback(null, collection);
return collection;
} catch(err) {
// if(err instanceof MongoError && callback) return callback(err);
if(callback) return callback(err); // <-- Line 466 - first time callback is called
throw err;
}
}
The second time it gets called, is by the async library when it needs to determine if it has to execute the next task in the array or invoke the final callback; by then, the callback is already null. The error you're seeing is being raised in the onlyOnce() function that is part of the async library:
function onlyOnce(fn) {
return function() {
if (fn === null) throw new Error("Callback was already called.");
var callFn = fn;
fn = null;
callFn.apply(this, arguments);
};
}
So the following code will raise the error:
async.waterfall([
function(callback) {
MongoClient.connect('mongodb://localhost:27017/test', (error, db) => {
if (error) {
console.error(error);
process.exit(-1);
}
callback(null, db);
});
},
function(db, callback) {
console.log('Querying albums collection...');
// This will cause the exception
db.collection('albums', callback);
},
function(db, albums, callback) {
console.log('Got albums...');
console.log('Querying photos collection...');
db.collection('photos', (error, photos) => {
if (error) {
return callback(error);
}
callback(null, db, albums, photos);
});
},
function(db, albums, photos, callback) {
console.log('Got photos...');
callback(null, 'DONE');
}
], function (error, results) {
console.error(error);
console.log(results);
process.exit(0);
});
While this version of the code will execute as expected:
async.waterfall([
function(callback) {
MongoClient.connect('mongodb://localhost:27017/test', (error, db) => {
if (error) {
console.error(error);
process.exit(-1);
}
callback(null, db);
});
},
function(db, callback) {
console.log('Querying albums collection...');
db.collection('albums', (error, albums) => {
if (error) {
return callback(error);
}
callback(null, db, albums);
});
},
function(db, albums, callback) {
console.log('Got albums...');
console.log('Querying photos collection...');
db.collection('photos', (error, photos) => {
if (error) {
return callback(error);
}
callback(null, db, albums, photos);
});
},
function(db, albums, photos, callback) {
console.log('Got photos...');
callback(null, 'DONE');
}
], function (error, results) {
console.error(error);
console.log(results);
process.exit(0);
});
/* Output:
Querying albums collection...
Got albums...
Querying photos collection...
Got photos...
null
DONE
*/
Hope this helps!

async.each not giving the result?

This is my code:
var shopId=[1,2,3,4];
async.each(shopId,function (item,callback) {
model.client.query("SELECT categoryId shopId=? ", [shopId], function (err, rows) {
if (rows.length > 0) {
var asyncData = rows;
//categoryId will be in the format [1,2,3,4]
async.each(asyncData,function (item, callback) {
var categoryId = item.categoryId;
async.series([
function (callback) {
model.client.query("select categoryName from category where categoryId = ?", [categoryId], function (err, data) {
callback(null, data);
});
}],
function (err, results) {
item.category = results[0];
callback();
});
},function (err) {
callback(asyncData); //getting data here
});
}
});
},function (err,result) {
res.json(result); //not getting data here
});
I am new to aysnc methods. I am not able to pass the result to final function.
Supposing that there is a shop table, you can join your tables to save a query.
const sql = 'select categoryName from category as c inner join shop as s on c.categoryId = s.categoryId where s.shopId = ?'
then
const shopId = [1, 2, 3, 4];
const finalResult = [];
async.each(shopId, function(item, callback) {
model.client.query(sql, [shopId], function (err, rows) {
// do not forget error handling !
if (rows.length > 0) {
finalResult.push(rows);
return callback();
}
return callback(); // do not forget to return callback in every case
});
}, function(err) { // in async each callback, you only get errors
if (err)
return res.json(err); // or whatever error handling
res.json(finalResult);
});
Also, considering your code, I would advise to name differently your callbacks to avoid errors or forgets. i.e.
async.each([], function(item, cbkGlobal) {
async.each([], function(item, cbkEach) {});
});
And async.series is usefull when you want multiple functions to get execute in order. But here you are executing only one function...

Async waterfall with mongo db

Probably just a basic concept of async. I'd like to check on how we can pass the array variable from mongo result(docs) to the second function. In below code the second console.log doesn't give any output.
// Retrieve
var db = require('monk')('localhost/db');
var async = require('async');
console.log('start');
async.waterfall([
function(callback) {
var test = db.get('test');
test.find({}, function(err, docs) {
console.log(docs); //OUTPUT OK
callback(docs);
});
},
function(docs, callback) {
console.log(docs); //NO OUTPUT
}
])
To give you a better understanding of the using multiple callbacks with the async module, let's illustrate this with an example from Seven Things You Should Stop Doing with Node.js of multiple operations with callbacks to find a parent entity, then find child entities that belong to the parent:
methodA(function(a){
methodB(function(b){
methodC(function(c){
methodD(function(d){
// Final callback code
})
})
})
})
With the async module, you can either use the series method to address the use of callbacks for nesting code of multiple methods which may result in Callback Hell:
Series:
async.series([
function(callback){
// code a
callback(null, 'a')
},
function(callback){
// code b
callback(null, 'b')
},
function(callback){
// code c
callback(null, 'c')
},
function(callback){
// code d
callback(null, 'd')
}],
// optional callback
function(err, results){
// results is ['a', 'b', 'c', 'd']
// final callback code
}
)
Or the waterfall:
async.waterfall([
function(callback){
// code a
callback(null, 'a', 'b')
},
function(arg1, arg2, callback){
// arg1 is equals 'a' and arg2 is 'b'
// Code c
callback(null, 'c')
},
function(arg1, callback){
// arg1 is 'c'
// code d
callback(null, 'd');
}], function (err, result) {
// result is 'd'
}
)
Now going back to your example, using the async waterfall method you could restructure your code to
async.waterfall([
function(callback) {
// code a
var test = db.get('test');
test.find({}, function(err, docs) {
if(err) callback(err);
console.log(docs); // OUTPUT OK
callback(null, docs);
});
}], function(err, result) {
if(err) callback(err);
console.log(result); // OUTPUT OK
}
])
You must always handle your errors. Try this!
// Retrieve
var db = require('monk')('localhost/db');
var async = require('async');
console.log('start');
async.waterfall([
function(callback) {
var test = db.get('test');
test.find({}, function(err, docs) {
if(err){
console.log(err);
callback(err);
}
else if(docs){
console.log(docs);
callback(null,docs);
}
else{
console.log("No error no docs");
callback(null);
}
});
},
function(err, docs) {
if(err)
console.log(err);
else{
console.log(docs);
}
}
])

Applying async nodejs

How to apply async series to this code... I am having trouble with the querying and using the results correctly... I need to take the user.friendStatus which is a list of all the friends a user have in the Friend collection and then, take each id of the friend and find the user info from the User collection...
UPDATE: Changed async.series to async.waterfall so I can pass one result to the next function. However, I am unsure how you call and pass the results from one to the next...
Here is my code:
exports.contactList = function(req, res) {
async.waterfall([
function(callback) {
var friends = [];
Friend.findOne({userId: req.signedCookies.userid}, function(err, users) {
if(err) {throw err};
for(var i = 0; i < users.friendStatus; i++) {
if(users.friendStatus[i].status === 3) { friends.push(users.friendStatus[i])};
}
});
callback(null, friends);
},
function(callback) {
var friendsinfo = [];
for(var i = 0; i < friends.length; i++) {
User.findbyID({_id: friends[i].fuID}, function(err, users) {
if (err) { throw err};
friendsInfo.push(users[i], friends[i].favorites);
});
}
callback(null, friendsinfo);
}
],
function(err, results) {
res.render('contactList', {title: 'Weblio', Friends: friendsinfo});
});
};
Here is an example of async.waterfall from https://github.com/caolan/async#waterfall I added more comments to me make it clear
async.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
// arg1 is equal 'one'
// arg2 is equal 'two
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
[edit] applied to your code...(notice is just add friends param to the second function)
[edit] better indentation, and change results var to friendsinfo in the last callback
exports.contactList = function(req, res) {
async.waterfall([
function(callback) {
var friends = [];
Friend.findOne({userId: req.signedCookies.userid}, function(err, users) {
if(err) {throw err};
for(var i = 0; i < users.friendStatus; i++) {
if(users.friendStatus[i].status === 3) { friends.push(users.friendStatus[i])};
}
});
callback(null, friends);
},
function(friends, callback) {
var friendsinfo = [];
for(var i = 0; i < friends.length; i++) {
User.findbyID({_id: friends[i].fuID}, function(err, users) {
if (err) { throw err};
friendsInfo.push(users[i], friends[i].favorites);
});
}
callback(null, friendsinfo);
}
],
function(err, friendsinfo) {
res.render('contactList', {title: 'Weblio', Friends: friendsinfo});
});
};

Resources