Async parallel final callback doesn't fire - javascript

I do have a async parallel block to execute two queries into MongoDB.
on each step of function(callback) I have a valid return result, no any errors are firing. I did debug and all steps are working except that final callback not firing and I can't event reach with breakpoint to its condition.
Does anybody have idea what that may happen?
async.parallel([
// first Query
function(callback){
this._game.findOne({'_id': gameId}, function(err, result){
if(err){return callback(err);}
else if(result) {
callback(null, result);
}
});
},
// second Query
function(callback){
this._player.findOne({'_id': playerId}, function(err, result){
if(err){return callback(err);}
else if(result) {
callback(null, result);
}
});
}
],
// Final callback to send all 2 Results -> which is not working...
function(error, results){
if(results){
res.status(200);
res.send(results);
}
}
);`

You're not dealing with the possibility that Mongo doesn't find any results. In this case it will call the callback with err and result as null. When this happens the functions that you're running in parallel don't call their callbacks, so the final function never runs. What you do in this case is up to you but you need to call the callback function with something.
The reason that you're not getting any results is most likely that playerId and gameId are strings and _id is by default an ObjectId. Add a var ObjectId = require('mongodb').ObjectId;, then replace playerId with ObjectId(playerId) and you'll likely see results.

This is how i would debug it :
async.parallel([
// first Query
function(fq_callback){
this._game.findOne({'_id': gameId}, function(fq_err, fq_result){
if(fq_err){
console.log('err from _game.findOne');
return fq_callback(fq_err);
}
else if(fq_result) {
console.log('result from _game.findOne');
fq_callback(null, fq_result);
}
});
},
// second Query
function(sq_callback){
this._player.findOne({'_id': playerId}, function(sq_err, sq_result){
if(sq_err){
console.log('err from _player.findOne');
return sq_callback(sq_err);
}
else if(sq_result) {
console.log('result from _player.findOne');
sq_callback(null, sq_result);
}
});
}
],
// Final callback to send all 2 Results -> which is not working...
function(fn_error, fn_results){
console.log('in final callback');
if(fn_error) console.log('we have error');
if(fn_results){
console.log('we have results');
res.status(200);
res.send(results);
}else{
console.log('we have NO results');
}
}
);

Related

Can someone explain how callbacks are invoked in Node.js please?

I understand callback functions conceptually, but I don't understand how they are understand in Node.js and am quite confused by the syntax. Can someone give me a simple explanation for each line of code which is running? The code works, but I don't understand why.
var removeById = function(personId, done) {
Person.findByIdAndRemove(personId, function(err, data) {
if(err) {
done(err);
}
done(null, data);
});
};
Line by line explanation.
Line 1 (assume)
var removeById = function(personId, done) {
done is the formal identifier for callback that you'll pass later when you call removeById function
Line 2
Person.findByIdAndRemove(personId, function(err, data) {
findByIdAndRemove expects 2nd parameter to be a function with two parameters, first err, this will hold error and 2nd data, this will hold data/result
Line 4
done(err)
Line 6
Call your callback with error
done(null, data)
call your callback with first parameter as null (probably intended to signal there is no error) and data which would hold the data/result
Extra note:
The callback you pass to removeById should also (preferably, if you're not doing anything else with it) expect 2 parameters, same as callback passed to findByIdAndRemove
The Person.findByIdAndRemove is basically something like :
Person.findByIdAndRemove = function(personId, callback) {
// The code is executed
// call function on error | success
callback(err, data);
};
The callback you want to execute should be something like:
const done = function(err, data) {
if(err) {
console.log(err);
}
console.log(data);
}
Your code :
var removeById = function(personId, done) {
Person.findByIdAndRemove(personId, function(err, data) {
if(err) {
done(err);
}
done(null, data);
});
};
Usage:
removeById(3, done);

Executing multiple HTTP requests sequentially using async.js

How can I execute multiple HTTP requests sequentially using async.js . I checked the async.js documentation but couldn't figure out, how to do that. I want to achieve the same thing as below code using async.js callback style.
var http = require('http');
var Q = require('q');
var URL="http://localhost:3000";
var getPromise=function(url) {
var deferred = Q.defer();
var req = http.get(url, function(response) {
if(response.statusCode < 200 || response.statusCode > 299){
deferred.reject(new Error('ErrorCode '+response.statusCode))
}
var result="";
response.on('data',function(chunk){result +=chunk;} )
response.on('end',function(){deferred.resolve(result);} )
});
req.on('error',function(err){
console.error('Error with the request:', err.message);
deferred.reject(err);
});
req.end();
return deferred.promise;
}
getPromise('http://localhost:3000/olympic/2016/ranking/4')
.then(function(data){
console.log("Response 1 "+data)
return getPromise(URL+'/iso/country/'+JSON.parse(data).Country);
})
.then(function(data){
console.log("Response 2 "+data)
return getPromise(URL+'/olympic/2016/medal/'+JSON.parse(data).iso);
})
.then(function(data){
console.log("Response 3 "+data)
})
.catch(function(err){
console.log(err)
});
I got it, I needed async.waterfall it takes an array of functions and execute them one by one. Also we can pass result from previous function execution to the next one
var async = require('async');
async.waterfall([
function task1(done) {
console.log('start!');
setTimeout(function(){
console.log("T1 Complete");
// <- set value to passed to step 2
done(null, 'Value from step 1');
},5000);
},
function task2(task1Result, done) {
console.log(task1Result);
setTimeout(function(){
console.log("T2 Complete");
// <- set value to passed to step 3
done(null, 'Value from step 2');
},1000);
},
function task3 (task2Result, done) {
console.log(task2Result);
setTimeout(function(){
console.log("T3 Complete");
// <- no value set for the next step.
done(null);
},100);
}
],
function (err) {
if (err) {
throw new Error(err);
} else {
console.log('No error happened in any steps, operation done!');
}
});
Looking over the code a bit and trying to understand it more, I believe that async.waterfall is the function you'll need. What this will do is run each function in order, passing its results to the next function in the sequence. Here's an example:
async.waterfall([
function(callback)
{
// Function 1: do request here...
callback(null, val); // replace null with a value if you want the waterfall to error and go straight to the end
},
function(val, callback) {
// Function 2: do your second request here
callback(null, val1, val2, val3); // you can pass any number of variables you like, just make sure the next function in the sequence expects them
},
function(val1, val2, val3, callback)
{
// Function 3: do your third request here
callback(null, result);
} // this can go on for as long as you like
], function(err, result)
{
// this will be called immediately if the first parameter in any of the callbacks is not null, or when all the functions have run
});

Mongodb find() returns undefined (node.js)

Ive been playing around with mongodb in node.js. I have made a basic collection with some data (i know its there ive checked). When I try to run a find() on the collection it returns undefined. I dont know why this is. The code is below:
function get_accounts(){
var MongoClient = mongodb.MongoClient;
var url = "url";
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
//HURRAY!! We are connected. :)
console.log('Connection established to database');
var collection = db.collection('accounts');
collection.find().toArray(function(err, docs) {
console.log("Printing docs from Array")
docs.forEach(function(doc) {
console.log("Doc from Array ");
console.dir(doc);
});
});
console.log("mission complete");
}
db.close();
}
);
}
If you know why this is happening i would like to hear your thoughts. thanks! The database is a mongolab hosted database if that makes any difference.
You are getting an undefined value because of the asynchronous nature of node.js, nowhere in your code exists logic that tells the console.log statement to wait until the find() statement finishes before it prints out the documents. You have to understand the concept of callbacks in Node.js. There are a few problems here, though, that you could fix. A lot of people getting started with node have the tendency to nest lots of anonymous functions, creating the dreaded "pyramid of doom" or callback hell. By breaking out some functions and naming them, you can make it a lot cleaner and easier to follow:
var MongoClient = require("mongodb").MongoClient
// move connecting to mongo logic into a function to avoid the "pyramid of doom"
function getConnection(cb) {
MongoClient.connect("your-mongo-url", function(err, db) {
if (err) return cb(err);
var accounts = db.collection("accounts");
cb(null, accounts);
})
}
// list all of the documents by passing an empty selector.
// This returns a 'cursor' which allows you to walk through the documents
function readAll(collection, cb) {
collection.find({}, cb);
}
function printAccount(account) {
// make sure you found your account!
if (!account) {
console.log("Couldn't find the account you asked for!");
}
console.log("Account from Array "+ account);
}
// the each method allows you to walk through the result set,
// notice the callback, as every time the callback
// is called, there is another chance of an error
function printAccounts(accounts, cb) {
accounts.each(function(err, account) {
if (err) return cb(err);
printAccount(account);
});
}
function get_accounts(cb) {
getConnection(function(err, collection) {
if (err) return cb(err);
// need to make sure to close the database, otherwise the process
// won't stop
function processAccounts(err, accounts) {
if (err) return cb(err);
// the callback to each is called for every result,
// once it returns a null, you know
// the result set is done
accounts.each(function(err, account) {
if (err) return cb(err)
if (hero) {
printAccount(account);
} else {
collection.db.close();
cb();
}
})
}
readAll(collection, processAccounts);
})
}
// Call the get_accounts function
get_accounts(function(err) {
if (err) {
console.log("had an error!", err);
process.exit(1);
}
});
You might have to add an empty JSON object inside the find.
collection.find({})
Documentation can be found here.
You must enter this code in an async function and you will be fine here data is the your desired value and you must use promises to not make your code look messy.
var accountCollection = db.collection('accounts);
let data = await accountCollection.find().toArray.then(data=>data).catch(err=>err);

Multiple Call backs with one response -Node JS

I am getting a request from client that request needs to select the data from two tables , from one table i need to select 1000 records in table two i need to select only two records, handling it with a call back . I want the response that needs to send to my client after the two callback function executes , How to achieve this in node js?
You have following options:
Follow Node style callback pattern. Also read, Callback-hell
Follow promises.
Callback pattern
var f = function(somedata, cb){
// pick user from db
pickUserFromTable1(somedata, function(err, users){
// callled on completion of pickUserFromDB
if(err){
//handle error.
cb(err, null);
return;
}
pickUserFromTable2(users, function(err, finallySelectedUsers){
// called on completion of pickUserFromTable2
if(err){
// handle error.
cb(err, null);
// returning is important. or use else part of if to separate the case
return;
}
// call initiator's callback function on completion of both pickUserFromTable1 and pickUserFromTable2
cb(null, finallySelectedUsers);
});
})
}
Note: This approach is good for short project but leads to some maintenance issue (callback hell) if dependency chain is lengthy.
Welcome to callback hell! You just need to build pyramide form callbacks!
Example:
function getDataFromDB (query, successCb, errorCb) {
executeFirstQuery(query, function(firstQueryData) {
console.log('First query ok! Now execute second.');
executeSecondQuery(query, function(secondQueryData) {
console.log('Second query ok! Now execute successCb().');
successCb([firstQueryData, secondQueryData]);
}, function(error) {
errorCb("Error in second query");
})
}, function(error) {
errorCb("Error in first query");
});
}
function executeFirstQuery (query, successCb, errorCb) {
console.log("Execute first query!");
successCb("Some data from first query");
// errorCb("Some Error from first query");
}
function executeSecondQuery (query, successCb, errorCb) {
console.log("Execute second query!");
successCb("Some from second query");
// errorCb("Some Error from second query");
}
getDataFromDB("Some sql", function(data) {
console.log(data);
}, function(error) {
console.error(error);
});
Or use a Promises: http://documentup.com/kriskowal/q/

My callbacks are wrong - I am returning my response to my error object - Nodejs

I am trying to learn node and understand how callbacks are working. I am doing this by trying to use the async lib. I am trying to hit my db with 2 separate calls and use the async lib to let me know when my object is ready to build. Here is my aysnc code:
async.parallel({
one: function(callback) {
getUser(id, callback);
},
two: function(callback) {
getUserServices(id, callback);
}
}, function(err, results) {
if(err) {
console.log(err);
new Error();
}
res.json(result);
});
Here are what my functions look like where I am calling the db. There are both basically the same function:
var getUser = function(id, callback) {
var query = client.query('SELECT * FROM USERS WHERE USER_ID=$1', [id]);
query.on('row', function(row, result) {
result.addRow(row);
});
query.on('end', function(result) {
callback(result);
});
};
My code hits the db and returns the user, but when it goes back up to the async code the user is in the err object. What am I doing wrong? How do I properly set up callbacks?
As pointed by damphat, your code should be
//additionally, handle errors
query.on('error', function(err){
callback(err) // The first argument to callback should be an error object
})
query.on('end', function(result){
callback(null, result) //passing null error object
})

Categories

Resources