handling asynchronous call backs in nodejs - node.js

I am newbie to nodejs.It's very hard to handle callbacks at nodejs level. I have code like this,
getItems(request,function(jsonObject){
var itemData={};
var itemDetails=new Array();
for(var i=0;i < jsonObject.length;i++){
getItemDetails(jsonObject[i].value.item_id,function(jsonObject){
itemDetails.push(jsonObject);
});
}
itemData["itemDetails"]=itemDetails;
response.contentType("application/json");
response.send({"data":itemData});
});
while executing the above code, the for loop is continuing with out getting callback from getItemDetails method and response sent to client. My requirement is the loop will wait until getting the call back from the getItemDetails then response should send.
I have tried with process.nextTick(), but i am unable to find where i have to use that process.nextTick().. Please anybody provide suggestions.
Thanks in advance.

You need to send the response only after you get all the items, so modify your code like so:
getItems(request,function(jsonObject) {
var itemData = {},
itemDetails = [],
itemsLeft = len = jsonObject.length,
i;
function sendResponse(itemDetails) {
itemData["itemDetails"] = itemDetails;
response.contentType("application/json");
response.send({ "data": itemData });
}
for (i = 0; i < len; i++) {
getItemDetails(jsonObject[i].value.item_id, function(jsonObject) {
itemDetails.push(jsonObject);
// send response after all callbacks have been executed
if (!--itemsLeft) {
sendResponse(itemDetails);
}
});
}
});
Note: I've used itemLeft here since it's a more generic way to solve these kind of problems, but Ianzz approach is also ok since you can compare the length of the two arrays.

You can't get the loop to wait, but you can modify your code to get the behavior you are expecting:
getItems(request,function(outerJsonObject){
var itemData={};
var itemDetails=new Array();
for(var i=0;i < outerJsonObject.length;i++){
getItemDetails(jsonObject[i].value.item_id,function(innerJsonObject){
itemDetails.push(innerJsonObject);
if (itemDetails.length == outerJsonObject.length) {
// got all item details
itemData["itemDetails"]=itemDetails;
response.contentType("application/json");
response.send({"data":itemData});
}
});
}
});

Related

synchronous execution of methods

I have below snmp call in my code, and am executing this in api call and need to get array of values and return it to the client side. but i am facing issue in executing this lines synchronously, my res.json executing first before getting vales from session.subtree
var shelf = new Array();
function doneCb (error) {
console.log("donecb");
console.log("shelf -----",shelf);
}
function feedCb (varbinds) {
console.log("feed cb");
shelf = [];
for (var i = 0; i < varbinds.length; i++) {
if (snmp.isVarbindError (varbinds[i]))
console.error (snmp.varbindError (varbinds[i]));
else {
var temp = varbinds[i].oid.trim(".");
shelf.push({'id':temp[temp.length-1], 'name':shelfmap[varbinds[i].value.toString()]});
console.log (varbinds[i].oid + "|" + shelfmap[varbinds[i].value.toString()]);
}
}
}
router.get('/getShelves/:r',function(req,res){
shelf = [];
session.subtree (oid, maxRepetitions, feedCb, doneCb);
res.json(shelf);
});
since feedcb and donecb are internal methods of net-snmp module i am not able to rewrite the functions to return the values, I tried with sync and async waterfall model, but it is not working as excepted, kinldy someone suggest some way to execute the method synchronously in node JS. Thanks in advance.

Mongoose promise built in but not working?

Or quite possibly I am doing it wrong, in fact, more than likely I am doing it wrong.
Have a table which contains a "tree" of skill, starting at the root level and may be as deep as ten levels (only two so far), but I want to return it as one big fat JSON structure, so I want to ask the database for each set of data, build my structure then ask for the next level.
Of course if I just send of my requests using mongoose, they will come back at any time, as they are all nice asyncronous calls. Normally a good things.
Looking at the documentation for Mongoose(using 4.1.1) it seems like it has a promise built in, but whenever I try to use it the api call throws a hissy fit and I get a 500 back.
Here is my simple function:
exports.getSkills = function(req,res) {
console.log("Will return tree of all skills");
for (var i = 0; i<10; i++){
var returnData = [];
console.log("Lets get level " + i );
var query = Skill.find({level: i });//The query function
var promise = query.exec; //The promise?
promise.then(function(doc) { //Totally blows up at this point
console.log("Something came back")
return "OK";
});
}
}
The Mongoose documentation on the subject can be found here
http://mongoosejs.com/docs/api.html#promise_Promise
var promise = query.exec;
// =>
var promise = query.exec()
exports.getSkills = function(req,res) {
console.log("Will return tree of all skills");
var p;
for (var i = 0; i < 10; i ++) {
if (i == 0 ) {
p = Skill.find({level:i}).exec();
} else {
p.then(function (){
return Skill.find({level:i}).exec()
})
}
p.then(function (data) {
//deal with your data
})
}
p.then(function () {
// deal with response
})
}

async in for loop in node.js without using async library helper classes [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I am a beginner to node.js. I was trying out the examples from the 'learnyounode' tutorial. I am trying to write a program that takes three url parameters and fetches some data from those urls and displays the returned data in the order in which the urls were provided.
var http = require('http');
var bl = require('bl');
var url = [];
url[0] = process.argv[2];
url[1] = process.argv[3];
url[2] = process.argv[4];
var data = [];
var remaining = url.length;
for(var i = 0; i < url.length; i++){
http.get(url[i], function (response){
response.setEncoding('utf8');
response.pipe(bl(function (err, chunk){
if(err){
console.log(err);
}
else{
data[i] = chunk.toString();
console.log(data[i]);
remaining -= 1;
if(remaining == 0) {
for(var j = 0; j < url.length; j++){
console.log(data[j]);
}
}
}
}));
});
}
I have two console.log statements in the program. The output i get is as follows:
It'll be chunder where lets throw a ford. We're going durry where mad as a cooee
.
Shazza got us some apples with come a strides. Mad as a swag when get a dog up y
a roo. It'll be rapt piece of piss as cunning as a trackie dacks.
As cross as a bogged with watch out for the boardies. As cunning as a digger fla
min lets get some roo bar. As dry as a piker piece of piss he hasn't got a joey.
Lets throw a strides mate we're going digger.
undefined
undefined
undefined
It seems like the data is correctly fetched and stored in the 'data' array but it still displays undefined.
Any idea why this is happening?
Thanks in advance!
This is a very common issue in async programming in node.js or even in the browser. A main issue you have is that the loop variable i will not be what you want it to be some time later when the async callback is called. By then, the for loop will have run to the end of its loop and i will be at the end value for all response callbacks.
There are numerous ways to solve this. You can use a closure to close over the i value and make it uniquely available to each callback.
var http = require('http');
var bl = require('bl');
var url = [];
url[0] = process.argv[2];
url[1] = process.argv[3];
url[2] = process.argv[4];
var data = [];
var remaining = url.length;
for(var i = 0; i < url.length; i++){
// create closure here to uniquely capture the loop index
// for each separate http request
(function(index) {
http.get(url[index], function (response){
response.setEncoding('utf8');
response.pipe(bl(function (err, chunk){
if(err){
console.log(err);
}
else{
data[index] = chunk.toString();
console.log(data[index]);
remaining -= 1;
if(remaining == 0) {
for(var j = 0; j < url.length; j++){
console.log(data[j]);
}
}
}
}));
});
})(i);
}
If you do much node.js programming, you will find that you probably want to learn how to use promises because they are very, very handy for controlling the flow and sequence of async operations.

Get the result at the right time with node

i am new to node.js, i followed some tutorial.
I don't know how to get the array "result" only when the call in the function end.
app.get('/api/email/check/:email',function (request,response){
var email = request.params['email'];
var result = Array();
for (var i = 0; i < 2; i++) {
existence.check(email, function(err,res){
result[i]=res; console.log({"Result":res});
});
};
response.send(result); // Problem is that i get: []
});
I got the log but the result is an empty array because it's called before the functions ends. Is there a nice way to resolve this ? without counting the "i".
You can put
response.send(result);
out of the for loop.
Because response.send() is a async method so before the for loop ends, response has ended before.

How to get rid of the asynchoronous code here

I have been trying to retrieve the data using the MongoJS driver for node.js.The Code which I am using is as follows
req.on('end', function(){
var decodedBody = querystring.parse(fullBody);
story=decodedBody.name;
var z=new Array();
console.log(story);
res.writeHead(200,{'Content-Type': 'text/html'});
res.write('<html><body>');
db.frames.find({str_id:story}).toArray(function(err,doc){
console.log(doc);
for(var t=0;t<doc.length;t++)
{
var picid=doc[t].pic_id;
console.log(picid);
db.pictures.find({_id:picid}).toArray(function(err,pic){
res.write('<img src="'+pic[0].name+'"/>');
});
}
})
res.end('</body></html>');
});
The problem here is that because of the asynchronous nature of the code the response gets ends first and then the code inside the block of database gets executed and because of that nothing gets displayed on the browser i.e an image in this case .Thankx in advance.
Don't fight the asynchronous nature of node.js, embrace it!
So you should fire off all your requests, marking each one as completed when the response arrives. When all requests are completed, render your images and body/html closing tags.
I don't usually work with node.js, so I can make some mistakes, but it may look like this:
res.write('<html><body>');
db.frames.find({str_id:story}).toArray(function(err,doc){
console.log(doc);
var completed = {};
for(var t = 0; t < doc.length; t++) {
var picid = doc[t].pic_id;
completed.picid = false;
console.log(picid);
db.pictures.find({_id: picid}).toArray(function(err, pic) {
// mark request as completed
completed.picid = pic;
// check if all requests completed
var all_finished = true;
for(var k in completed) {
if(completed[k] === false) {
all_finished = false;
break;
}
}
// render final markup
if(all_finished) {
for(var k in completed) {
var pic = completed[k];
res.write('<img src="'+pic[0].name+'"/>');
}
res.end('</body></html>);
}
});
}
})
just put the res.end('</body></html>'); inside your db.frames.find function. Check when you reached doc.length - 1 and then send the end command.

Resources