callback hell in loop of queries - node.js

i'm trying to get a list of distinct values and count of items. like this:
sheet_kinds: [
"cars" (10 items),
"computers" (23 items),
"utilities" (88 items)
],
so the query to get distinct values is ok. my code:
getValues:function(next){
Sheet.find().distinct('kind', function(err, rs){
for (var i = 0; i < rs.length; i++) {
Sheet.count({kind:rs[i]}, function(err, c){
next(null, rs); <====== THIS IS NOT GOOD
});
};
});
}
I cannot run next() inside a loop, I know. But how can I get a full list of count values and run next() only after all items back?

In this kind of scenario you'd be much better off using async
Install
npm install --save async
Require
var async = require('async');
Use
getValues:function(next){
Sheet.find().distinct('kind', function(err, rs){
async.map(rs, function (item, done) {
Sheet.count({kind:item}, done);
}, next);
});
}
Details
getValues:function(next){
Sheet.find().distinct('kind', function(err, rs){
// async.map is used to map a collection asynchronously
// the cb will be invoked once for each item in rs
async.map(rs, function (item, done) {
// the done callback needs to be invoked exactly once
// in this case, we just pass it to count, since
// the (err, count) result is exactly what we want (getting us the count)
Sheet.count({kind:item}, done);
// next is invoked with the err, if any, and
// the resulting map (an array of counts)
}, next);
});
}
Update
Addresses question in comments
getValues:function(next){
Sheet.find().distinct('kind', function(err, rs){
async.map(rs, function (item, done) {
Sheet.count({kind:item}, function (err, count) {
done(err, {kind:item,count:count});
});
}, next);
});
}

Related

node js + async parallel + function call method not working

I have functions in different place.(this is common function it will use many place.
So I tried to call the function using async parallel but its not working.
I attached the my code following:L
var userId = 33;
async.parallel({
video(userId, callback) {
callback(null, callback);
},
video1(userId, callback) {
callback(null, callback);
}
}, function(err, results) {
console.log(results);
});
function video (userId, callback) {
client.query("select youtube_id,title,id,thumbnail,search_item from resource order by random() limit 1 ", function(err, video) {
callback(err, video);
});
}
function video1(userId, callback) {
client.query("select youtube_id,title,id,thumbnail,search_item from resource1 order by random() limit 1 ", function(err, video1) {
callback(err, video1);
});
}
The {fnName()} syntax is a function declaration. You want to use a function call ... in fact you can just use async.parallel({video, video1}, (err, results) => console.log(results) to achieve the same effect... except that you also need to pass in userId. So what you actually intend is to call the function rather than declare it:
async.parallel([
callback => video(userId, callback),
]);
You need to pass function references to async.parallel() and you need to pass an array, not an object. You want the code to have this form:
async.parallel([fn1, fn2], finalCallback);
See the async.parallel() doc here for specific examples of the syntax you want to use.
Where fn1, fn2 and finalCallback are all function references that can be called by the async.parallel() infrastructure, not something you call yourself.
Change your code to this:
async.parallel([
function(callback) {
client.query("select youtube_id,title,id,thumbnail,search_item from resource order by random() limit 1 ", function(err, video) {
callback(err, video);
}),
},
function(callback) {
client.query("select youtube_id,title,id,thumbnail,search_item from resource order by random() limit 1 ", function(err, video1) {
callback(err, video1);
});
}
], function(err, results) {
console.log(results);
});
Presumably, your two database queries should be different somehow (your code showed them identical).
If your functions are already defined somewhere, then you can pass an anonymous function reference that will call them. You need to do this if your functions don't have the exact same calling convention that async.parallel() requires. You can do that like this:
async.parallel([function(callback) {
video(userId, callback);
}, function(callback) {
video1(userId, callback);
}], function(err, results) {
console.log(results);
});

Make Node.js code synchronous in Mongoose while iterating

I am learning Node.js; due to asynchronous of Node.js I am facing an issue:
domain.User.find({userName: new RegExp(findtext, 'i')}).sort('-created').skip(skip).limit(limit)
.exec(function(err, result) {
for(var i=0;i<result.length;i++){
console.log("result is ",result[i].id);
var camera=null;
domain.Cameras.count({"userId": result[i].id}, function (err, cameraCount) {
if(result.length-1==i){
configurationHolder.ResponseUtil.responseHandler(res, result, "User List ", false, 200);
}
})
}
})
I want to use result in Cameras callback but it is empty array here, so is there anyway to get it?
And this code is asynchronous, is it possible if we make a complete function synchronous?
#jmingov is right. You should make use of the async module to execute parallel requests to get the counts for each user returned in the User.find query.
Here's a flow for demonstration:
var Async = require('async'); //At the top of your js file.
domain.User.find({userName: new RegExp(findtext, 'i')}).sort('-created').skip(skip).limit(limit)
.exec(function(err, result) {
var cameraCountFunctions = [];
result.forEach(function(user) {
if (user && user.id)
{
console.log("result is ", user.id);
var camera=null; //What is this for?
cameraCountFunctions.push( function(callback) {
domain.Cameras.count({"userId": user.id}, function (err, cameraCount) {
if (err) return callback(err);
callback(null, cameraCount);
});
});
}
})
Async.parallel(cameraCountFunctions, function (err, cameraCounts) {
console.log(err, cameraCounts);
//CameraCounts is an array with the counts for each user.
//Evaluate and return the results here.
});
});
Try to do async programing allways when doing node.js, this is a must. Or youll end with big performance problems.
Check this module: https://github.com/caolan/async it can help.
Here is the trouble in your code:
domain.Cameras.count({
"userId": result[i].id
}, function(err, cameraCount) {
// the fn() used in the callback has 'cameraCount' as argument so
// mongoose will store the results there.
if (cameraCount.length - 1 == i) { // here is the problem
// result isnt there it should be named 'cameraCount'
configurationHolder.ResponseUtil.responseHandler(res, cameraCount, "User List ", false, 200);
}
});

Waiting for operation to finish in node.js

I'm new to Node.js. I'm trying to use the Node Redis plugin. What I'm trying to do is get the list of key/value pairs from a REDIS database. The asynchronous nature of Node is creating a challenge for me. When I try to print the key/value pairs, my array is empty. I think its because my query to the database hasn't completed yet.
The reason I'm creating an array first instead of just printing out the key/value pairs, is because I want to sort the results alphabetically by either key or value. Currently, I"m trying the following:
redisClient.on('connect', function() {
var pairs = [];
redisClient.keys('*', function (err, keys) {
if (err) { return console.log(err); }
for(var i = 0, len = keys.length; i < len; i++) {
(function(key) {
redisClient.get(key, function(err, v) {
pairs.push({ key: key, value: v });
});
})(keys[i]);
}
});
// List of key/value pairs ordered alphabetically
console.log(pairs);
});
Can someone tell me how to get beyond this issue? Thank you
You are right in your assertion that when console.log is executed, nothing has happened yet. To deal with this kind of problem in Node.js, some people like to use modules like async and others prefer to use promises (a popular library for promises is Q).
Here is a solution using async.
var async = require('async');
redisClient.on('connect', function() {
redisClient.keys('*', function (err, keys) {
if (err) { return console.log(err); }
async.map(keys, function(key, callback) {
redisClient.get(key, function(err, value) {
callback(null, {key: key, value: value});
});
}, function(err, pairs) {
console.log(pairs);
});
});
});
async just happens to have a map function that takes an array, applies a function asynchronously to each elements and creates an array with the results.
You need to use recursion:
do = function(i, data, callback){
// if the end is reached, call the callback
if(data.length === i+1)
return callback()
obj = data[i]
redisClient.get(key, function(err, v) {
pairs.push({ key: key, value: v });
i++;
// call do with the next object
do(i, data, callback)
});
}
// Redis...
do(0, keys, function(){
// List of key/value pairs ordered alphabetically
console.log(pairs);
});

Async node.js data flow confusion

thanks for your help...struggling big time with how to handle this properly. I'm in async now, having given up on my ability to write the callbacks properly. I have snippet where I'm passing a set of random numbers (eachrecord) and passing them through to a mongoose call. Trying to create a data set from the multiple queries I pass.
My issue is that no matter what I've done for 4 hours, the "newarray" variable is always empty.
Thank you for your help -
async.forEach(arLimit, function(eachrecord, callback){
newarray = new Array;
var query = UGC_DB_Model.find({}).skip(eachrecord).limit(-1);
query.execFind(function (err, data) {
if (err)
console.log(err);
else {
newarray.push(data);
}
});
callback(null, newarray);
}, function(err, result) {
if (err) return next(err);
console.log("(it's empty): " + result);
});
There are several issues with your code:
async.forEach isn't meant to 'generate' results, that's what async.map is for;
you need to call the callback only when execFind is done, and not immediately after calling it;
your newarray is probably not necessary;
So try this instead:
async.map(arLimit, function(eachrecord, callback){
var query = UGC_DB_Model.find({}).skip(eachrecord).limit(-1);
query.execFind(function (err, data) {
if (err)
callback(err); // pass error along
else {
callback(null, [ data ]);
// although I think you mean this (because 'data' is probably an array already)
// callback(null, data);
}
});
}, function(err, result) {
if (err) return next(err);
console.log("(it's empty): " + result);
});

Object not being passed into callback

I am using filterSeries of the async npm but when I call the truthy next on the object, for some reason it only passes the user and not the parts trying to be taken out of the query...
If you notice what is wrong with my code or have a more efficient way of going about this, because I also, heard that looping through each users and calling a query is a bad idea instead to do a $in or something but not sure how.
The main thing is I want to combine both documents and feed it back as data...
Here is the code:
exports.searchContactPost = function(req, res) {
if(req.body.searchContacts === '') { res.send('Oops you searching for nothing, well here is nothing!'); };
async.waterfall([
function(callback) {
User.find({$or:[
{firstName: req.body.searchContacts.toLowerCase()},
{lastName: req.body.searchContacts.toLowerCase()},
{email: req.body.searchContacts.toLowerCase()}]
}, function(err, users) {
if(err || users.length === 0) { res.send(err);}
callback(null, users)
});
},
function(users, callback) {
async.filterSeries(users, function(user, next) {
console.log(user);
Friend.findOne({userId: req.signedCookies.userid, friend_id: user}, function(err, friend) {
if(err) {
console.log("houston we got a problem.")
}
var object = {'fav': friend.favorites, 'notes': friend.notes, 'labels': friend.labels, 'user': user, 'status':friend.friend_status};
console.log(friend);
next(object.status === 3);
})
}, function(friendResults){
console.log(friendResults);
callback(null, friendResults);
});
}
],
function(err, results) {
res.render('contactListResults', {title: 'Weblio', friendsFound: results});
});
};
The async filter function takes an array of items and filters out items from that array based on a true or false callback. Therefore you will get back a subset of the original array passed into the filter. Which in this case is users, I believe your trying to build up a friend object and return it, which won't work. What you should do instead is just query the database for all friends of the appropriate status instead of using a filter.

Resources