Waiting for operation to finish in node.js - 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);
});

Related

Issue with asynchronous mongodb query

I am trying to loop through an array and find the amount of tickets assigned to each person.
Unfortunately, I noticed that my taskcount is getting the same values but in different order, because of its asynchronous nature.
Some queries might take long and so the ones that gets finished first gets inserted and hence my array has the same values but in different order. Now, I want to avoid that and make it so, that once a query gets completed, only then the next value from the array is being picked up and pushed to search from the db. How can i modify my existing code.
exports.find_task_count = function(callback) {
var names = ['Evan', 'Surajit', 'Isis', 'Millie', 'Sharon', 'Phoebe', 'Angel', 'Serah']
var taskcount = []
var resultsCount = 0;
for (var i = 0; i < names.length; i++) {
_tasks.find({'assignee': names[i]}, function (err, tickets) {
resultsCount++
if (err) {
console.log(err)
return callback(err)
} else {
taskcount.push(tickets.length)
if (resultsCount === names.length) {
return callback(taskcount);
taskcount=[]
}
}
})
}
}
You can use the async module designed to handle such scenarios.
I have updated the code as follows
var async = require('async');
exports.find_task_count = function (callback) {
var names = ['Evan', 'Surajit', 'Isis', 'Millie', 'Sharon', 'Phoebe', 'Angel', 'Serah'];
async.map(names, function (name, iterateeCallback) {
_tasks.find({ 'assignee': name }, function (err, tickets) {
if (err) {
return iterateeCallback(err);
}
return iterateeCallback(null, tickets.length);
});
}, function (error, results) {
if (error) {
return callback(error);
}
return callback(null, results);
});
}
As per the documentation of async
Note, that since this function applies the iteratee to each item in parallel, there is no guarantee that the iteratee functions will complete in order. However, the results array will be in the same order as the original coll.
if you still want to process the array in series use mapSeries instead of map in the above code

Foreach loop with mongoDB call / node.js

result.forEach(element => {
//Get each element
console.log("LOOP");
dbo.collection("users").findOne({email: emailGiven, "friends.email": element.email},function(errT, resultT) {
if (errT){
console.log("Query Error Inside!");
res.status(errT.status); // or use err.statusCode instead
console.log(errT);
//db.close();
//return res.send(errT.message);
}
else {
if (resultT) {
var oneUser = {
email: element.email,
username: element.username,
fullName: element.fullName,
status: resultT
};
//console.log(resultT);
foundUsers.push(oneUser);
} else {
//Not found means not added or pending
var oneUser = {
email: element.email,
username: element.username,
fullName: element.fullName,
status: 0
};
foundUsers.push(oneUser);
//console.log(emailGiven + " " + element.email)
console.log(oneUser);
}
}
});
});
i have an object array for each elemant i would like to do mongoDB call for each element and depending on the results i wanna push the results in an array as im doing, the problem is that mongoDb is async so my main thread finished before i can push results to the array foundUsers, how may i fix this issue?
As you said, need to do handle an asynchronous operation into a synchronous loop. For doing this, you can use async library. It is so useful in such operatinos.
Just install async module in your project first
npm install --save async
Afterwards, you can do sth like this:
// for use with Node-style callbacks...
var async = require("async");
var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"};
var configs = {};
async.forEachOf(obj, (value, key, callback) => {
fs.readFile(__dirname + value, "utf8", (err, data) => {
if (err) return callback(err);
try {
configs[key] = JSON.parse(data);
} catch (e) {
return callback(e);
}
callback();
});
}, err => {
if (err) console.error(err.message);
// configs is now a map of JSON data
doSomethingWith(configs);
});
For working with this library, it uses async.forEachOf function instead of simple forEach loop. Three parameters is sent to this function.
The 1st parameter that is passed to async.forEachOf is an array to iterate over it (obj).
The 2nd parameter is a callback function that apply over each item in obj.
The 3rd or the last parameter that is passed to async.forEachOf function, is another callback function too. It is called when iteration process over every item in obj has finished.

Redis keys are presented as strings, not objects

I want to get data from Redis session with nodejs - but i can't get the inner values of the objects....
this is a simple router
router.get('/page-user', function (req, res) {
console.log(redis_model.prototype.getAll());
})
and here is the model
redis_model.prototype.getAll = function () {
client.keys('*', function (err, keys) {
if (err) return console.log(err);
for(var i = 0, len = keys.length; i < len; i++) {
console.log(keys[i]);
}
});
};
So i'm getting
users
id:users
session:php:cf5myWFkDNEPwiRLpi6M1P6LqX1UPFtj //object
user:{58}
I'm trying to fetch data from the session key and i'm getting Undefined , like this:
redis_model.prototype.getAll = function () {
client.keys('*', function (err, keys) {
if (err) return console.log(err);
console.log(keys['session']); // tried also keys.session
});
};
The part that i can't figure out is why i get Type String for all the keys - like here:
redis_model.prototype.getAll = function () {
client.keys('*', function (err, keys) {
if (err) return console.log(err);
console.log(typeof keys[i]); result : //string,string, string,string
});
};
I've tried HGETALL to get the keys as objects but i still get undefined :
redis_model.prototype.getAll = function () {
client.hgetall("session",function(data){console.log(data)});
};
Here is a screenshot of the redis db...
Client.keys("*",function(err,keys){})
This just returns all the keys present in the redisDB which are basically strings
Instead you can use
client.get('key',function(err,data){})
inorder to get the result as an object it should be stored as a hash i.e
client.hmset("session",{'php':'cf5myWFkDNEPwiRLpi6M1P6LqX1UPFtj'}
client.hgetall("session",function(data){console.log(data.php)})
the above code gives you the value assigned to php.
Got it....
redis_model.prototype.getAll = function () {
client.get('session:php:VfAPTh_NLpBrcq3VGHTC8uT7c-sF4bQd', function(err,
result){
var foo = (JSON.parse(result))['user'];
console.log( foo);
});
};

NodeJS + redis gives weird results

Maybe the results ain't weird, but I started using Node 1-2 months ago so for me they are...
I have a loop which sorts out every other value of the array returned by hgetall (Redis command) and in that loop I call a function to get all values from another table with keys stored in the sorted array. This was more difficult to explain than I thought. Here's my code:
Pastebin: http://pastebin.com/tAVhSUV1 (or see below)
function getInfo (cn, callback) {
var anArray = [];
redis_client.hgetall('chat_info:' + cn, function (err, vals) {
if(err) { throw err; }
for(i in vals) {
anArray.push(vals[i]);
}
return callback(anArray);
});
}
redis_client.hgetall('chat_rooms:' + POST.chat_name, function (err, val) {
if(err) { throw err; }
var vars = [],
rArr = [];
for (i in val) {
vars.push(i);
}
for(var i = 0; i < vars.length; i += 1) {
if(i%2 === 0) {
getInfo(vars[i], function (hej) {
rArr.push(hej);
});
}
}
});
The callback from the call to getInfo() is executed after the entire loop. Am I missing out on something here? Because it can't do that, right? (when I use rArr (right after the loop) it's empty, nbBut if I log it in the callback it gets logged after everything else written after the loop)
Yes, that's probably normal.
Understand that callbacks are executed after the hgetall call. Which mean that when the redis functions receive somehting it will call the callbacks. In other words, all the callbacks can be executed later.
As javascript only works in one thread, the calls to hgetall should be blocking to be executed as they come in the for loop. But as you're more certainly using async IO. The for loop ends and then it will start calling each callbacks that were queued inside the javascript event loop.
Edit
Unfortunately, to achieve what you're trying to do, you should wrap your code inside many other callbacks. You can use this project to make it easier: https://github.com/caolan/async
You should be able to install it using npm install async.
You'd have to do something like that:
function getInfo (cn) {
return function(callback) {
var anArray = [];
redis_client.hgetall('chat_info:' + cn, function (err, vals) {
if(err) { throw err; }
for(i in vals) {
anArray.push(vals[i]);
}
return callback(anArray);
});
};
}
redis_client.hgetall('chat_rooms:' + POST.chat_name, function (err, val) {
if(err) { throw err; }
var vars = [],
rArr = [],
callbacks = [];
for (i in val) {
vars.push(i);
}
for(var i = 0; i < vars.length; i += 1) {
if(i%2 === 0) {
callbacks.push(getInfo(vars[i]));
}
}
async.series(callbacks, function (err, results) {
// Final code here
});
});

Return value in a async.forEach in node js?

I'm starting to learn node.js, and to aggregate multiple rss feeds in a single one, I'm fectching the feeds and then recreate a unique feed from the data I fecthed.
So, in order to handle multiple http requests asynchronously I use https://github.com/caolan/async#forEach which does the job.
But I can't figure how to return a value (the rss xml feed in my case).
Here is my code :
function aggregate(topic) {
async.forEach(topic.feeds,
function(item, callback) {
parseAndProcessFeed(item, callback);
},
function(err) {
// sort items by date
items.sort(function(a, b) {
return (Date.parse(b.date) - Date.parse(a.name));
});
var rssFeed = createAggregatedFeed();
console.log(rssFeed);
return rssFeed;
}
);
}
with console.log(rssFeed) I can see the rss feed, so I think I'm missing something obvious.
How can I return the rssFeed value?
Some help to get me back on seat would be great, thanks!
Xavier
You can't return value from asyncronious function. You need pass it into callback function. Like this:
function aggregate(topic, callback) {
async.forEach(topic.feeds,
function(item, callback) {
parseAndProcessFeed(item, callback);
},
function(err) {
// sort items by date
items.sort(function(a, b) {
return (Date.parse(b.date) - Date.parse(a.name));
});
var rssFeed = createAggregatedFeed();
callback(err, rssFeed);
}
);
}
aggregate(someTopic, function(err, result) {
// here is result of aggregate
});

Resources