I am trying to write a controller in sailsjs
module.exports = {
someFunction: function(req, res){
var userID = req.param('id')
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
ReceiverName = receiverName;
}
});
console.log("#####################################");
console.log(ReceiverName);
console.log("#####################################");
.
.
.
.
But when I deploy the controller, restart the sails app and call this function through the URL, I am getting the following error on the console:
error: Sending 500 ("Server Error") response:
ReferenceError: ReceiverName is not defined
I tried to declare the variable ReceiverName to be global, but still cant sort it out. I need guidance on making it global so that I assign value to it inside the else part and use it anywhere inside the controller.
Note 1:
However, when I am printing the value of the variable on console right inside the else block using the code:
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
console.log("#####################################");
console.log(receiverName);
console.log("#####################################");
}
});
the value is printed perfectly.
Note 2:
I am already making asynchronous call to retrieve the receiverName, so it is not a doubt about returning the response from asynchronous calls. I am looking for method to store the response in a variable that can be used further.
Your Note 2 is a little bit confusing so I'm not sure if you understand that the reason it does not work in your first example is because the User.findUserName() has not finished running before you execute your console.log().
Remember, the database calls are async. So any values you want to use from that database call have to run after it finishes. The only way to ensure that is to make sure your code runs inside the callback.
That is why the second example works. The code that needed the receiverName variable ran inside the callback. Try this, it is pretty much the same thing as your second example ...
User.findUserName(userID, function(err, receiverName){
if(err){
res.serverError(err);
}else{
doStuffWith(receiverName)
}
});
var dostuffWith = function(ReceiverName){
// all the code that you want to use ReceiverName should be in here.
console.log("#####################################");
console.log(ReceiverName);
console.log("#####################################");
}
You should do some reading on programming patterns with Node.js, async and callbacks. Like this
http://book.mixu.net/node/ch7.html
Related
So I'm making a web application and I'm trying to send variables to an EJS file but when they are sent out of the mongo functions they come out as undefined because it's a different scope for some reason. It's hard to explain so let me try to show you.
router.get("/", function(req, res){
var bookCount;
var userCount;
Books.count({}, function(err, stats){
if(err){
console.log("Books count failed to load.");
}else{
bookCount = stats;
}
});
User.count({}, function(err, count){
if(err){
console.log("User count failed to load.")
}else{
userCount = count;
console.log(userCount);
}
});
console.log(userCount);
//Get All books from DB
Books.find({}, function(err, allbooks){
if(err){
console.log("Problem getting all books");
}else{
res.render("index", {allbooks: allbooks, bookCount: bookCount, userCount: userCount});
}
});
});
So in the User.Count and Books.count I'm finding the number of documents in a collection which works and the number is stored inside of the variables declared at the very top.
After assigning the numbers like userCount i did console.log(userCount) which outputs the correct number which is 3, If was to do console.log(userCount) out of the User.count function it would return undefined, which is a reference to the declaration at the very top.
What is really weird is that Book.Find() has the correct userCount even though its a totally different function. The whole goal im trying to accomplish is doing res.render("index", {userCount: userCount}); outside of the Books.find(). I can do it but of course for some reason it passes undefined instead of 3. I hope this made a shred of sense.
I seem to have found a solution. but if anyone knows a different way I would love to know. So basically all you need to do is move the User.Count function outside of the router.get() function. Not completely sure about the logic of that but it works...
This is a classic asynchronous-operation problem: Your methods (Books.count, Books.find, User.count) are called immediately, but the callback functions you pass to them are not. userCount is undefined in your log because console.log is called before the assignment in the callback function is made. Your code is similar to:
var userCount;
setTimeout(function() {
userCount = 3;
}, 1000);
console.log(userCount); // undefined
User.count takes time to execute before calling back with the result, just like setTimeout takes the specified time to execute before calling its callback. The problem is JS doesn't pause and wait for the timeout to complete before moving on and calling console.log below it, it calls setTimeout, calls console.log immediately after, then the callback function is called one second later.
To render a complete view, you need to be sure you have all of the data before you call res.render. To do so you need to wait for all of the methods to call back before calling res.render. But wait, I just told you that JS doesn't pause and wait, so how can this be accomplished? Promise is the answer. Multiple promises, actually.
It looks like you are using Mongoose models. Mongoose has been written so that if you don't pass a callback function to your methods, they return a promise.
Books.count({}) // returns a promise
JS promises have a method then which takes a callback function that is called when the promise has been resolved with the value of the asynchronous method call.
Books.count({}) // takes some time
.then(function(bookCount) { // called when Books.count is done
// use the bookCount here
})
The problem is, you want to wait for multiple operations to complete, and multiple promises, before continuing. Luckily JS has a utility just for this purpose:
Promise.all( // wait for all of these operations to finish before calling the callback
Books.count({}),
User.count({}),
Books.find({})
)
.then(function(array) { // all done!
// the results are in an array
bookCount = array[0];
userC0unt = array[1];
allBooks = array[2];
})
router.post("/application_action", function(req,res){
var Employee = req.body.Employee;
var conn = new jsforce.Connection({
oauth2 : salesforce_credential.oauth2
});
var username = salesforce_credential.username;
var password = salesforce_credential.password;
conn.login(username, password, function(err, userInfo, next) {
if (err) { return console.error(err); res.json(false);}
// I want this conn.query to execute first and then conn.sobject
conn.query("SELECT id FROM SFDC_Employee__c WHERE Auth0_Id__c = '" + req.user.id + "'" , function(err, result) {
if (err) { return console.error(err); }
Employee["Id"] = result.records[0].Id;
});
//I want this to execute after the execution of above query i.e. conn.query
conn.sobject("SFDC_Emp__c").update(Employee, function(err, ret) {
if (err || !ret.success) { return console.error(err, ret);}
console.log('Updated Successfully : ' + ret.id);
});
});
I have provided my code above. I need to modify Employee in the conn.query and use it in conn.sobject. I need to make sure that my first query executes before 2nd because I am getting value from 1st and using in the 2nd. Please do let me know if you know how to accomplish this.
New Answer Based on Edit to Question
To execute one query based on the results of the other, you put the second query inside the completion callback of the first like this:
router.post("/application_action", function (req, res) {
var Employee = req.body.Employee;
var conn = new jsforce.Connection({
oauth2: salesforce_credential.oauth2
});
var username = salesforce_credential.username;
var password = salesforce_credential.password;
conn.login(username, password, function (err, userInfo, next) {
if (err) {
return console.error(err);
res.json(false);
}
// I want this conn.query to execute first and then conn.sobject
conn.query("SELECT id FROM SFDC_Employee__c WHERE Auth0_Id__c = '" + req.user.id + "'", function (err, result) {
if (err) {
return console.error(err);
}
Employee["Id"] = result.records[0].Id;
//I want this to execute after the execution of above query i.e. conn.query
conn.sobject("SFDC_Emp__c").update(Employee, function (err, ret) {
if (err || !ret.success) {
return console.error(err, ret);
}
console.log('Updated Successfully : ' + ret.id);
});
});
});
});
The only place that the first query results are valid is inside that callback because otherwise, you have no way of knowing when those asynchronous results are actually available and valid.
Please note that your error handling is unfinished since you don't finish the response in any of the error conditions and even in the success case, you have not yet actually sent a response to finish the request.
Original Answer
First off, your code shows a route handler, not middleware. So, if you really intend to ask about middleware, you will have to show your actual middleware. Middleware that does not end the request needs to declare next as an argument and then call it when it is done with it's processing. That's how processing continues after the middleware.
Secondly, your console.log() statements are all going to show undefined because they execute BEFORE the conn.query() callback that contains the code that sets those variables.
conn.query() is an asynchronous operation. It calls its callback sometime IN THE FUTURE. Meanwhile, your console.log() statements execute immediately.
You can see the results of the console.log() by putting the statements inside the conn.query() callback, but that is probably only part of your problem. If you explain what you're really trying to accomplish, then we could probably help with a complete solution. Right now, you're just asking questions about flawed code, but not explaining the higher level problem you're trying to solve so you're making it hard for us to give you the best answer to your actual problem.
FYI:
app.locals - properties scoped to your app, available to all request handlers.
res.locals - properties scoped to a specific request, available only to middleware or request handlers involved in processing this specific request/response.
req.locals - I can't find any documentation on this in Express or HTTP module. There is discussion of this as basically serving the same purpose as res.locals, though it is not documented.
Other relevants answers:
req.locals vs. res.locals vs. res.data vs. req.data vs. app.locals in Express middleware
Express.js: app.locals vs req.locals vs req.session
You miss the basics of the asynchronous flow in javascript. All the callbacks are set to the end of event loop, so the callback of the conn.query will be executed after console.logs from the outside. Here is a good article where the the basic concepts of asynchronous programming in JavaScript are explained.
I am new to mongoose.I am using Sails js, Mongo DB and Mongoose in my project. My basic requirement was to find details of all the users from my user collection. My code is as follows:
try{
user.find().exec(function(err,userData){
if(err){
//Capture the error in JSON format
}else{
// Return users in JSON format
}
});
}
catch(err){
// Error Handling
}
Here user is a model which contains all the user details. I had sails lifted my app and then I closed my MongoDB connection. I ran the API on DHC and found the following:
When I ran the API for the first time on DHC, the API took more than 30 sec to show me an error that the MongoDB connection is not avaliable.
When I ran the API for the second time, The API timed out without giving an response.
My Question here why is the try and catch block unable to handle such an error exception effectively in mongoose or is it something that I am doing wrong?
EDIT
My Requirement is that mongoose should display the error immediately if the DB connection is not present.
First let’s take a look at a function that uses a synchronous usage pattern.
// Synchronous usage example
var result = syncFn({ num: 1 });
// do the next thing
When the function syncFn is executed the function executes in sequence until the function
returns and you’re free to do the next thing. In reality, synchronous functions should be
wrapped in a try/catch. For example the code above should be written like this:
// Synchronous usage example
var result;
try {
result = syncFn({ num: 1 });
// it worked
// do the next thing
} catch (e) {
// it failed
}
Now let’s take a look at an asynchronous function usage pattern.
// Asynchronous usage example
asyncFn({ num: 1 }, function (err, result) {
if (err) {
// it failed
return;
}
// it worked
// do the next thing
});
When we execute asyncFn we pass it two arguments. The first argument is the criteria to be used by the function. The second argument is a callback that will execute whenever asyncFn calls the callback. asyncFn will insert two arguments in the callback – err and result). We
can use the two arguments to handle errors and do stuff with the result.
The distinction here is that with the asynchronous pattern we do the next thing within the callback of the asynchronous function. And really that’s it.
I try to understand how I can stop or exit on error in a Nodejs route.
In the code below, I check if header UID is sent and if the fiels group is also sent.
The problem is that nodejs continues to execute the rest of the code even if I use res.end () + return; I wish that Node Js stop everything when I display an error. Perhaps because I do not know much in Node Js I myself take it badly and I have to work otherwise. Can you explain to me and give me an example of how I should do?
var uid;
var group;
if (typeof req.headers['uid'] == 'undefined' || req.headers['uid'] == '') {
res.status(404);
res.json('user_id not set');
res.end();
return;
}
else
{
uid = req.headers['uid'];
User.find({_id:uid}).exec(function(err, data)
{
if(err){
res.status(404);
res.json('user not found');
res.end();
return;
}
});
}
if (typeof req.body.group == 'undefined' || req.body.group == '') {
res.status(500);
res.json('group not defined');
res.end();
return;
}
else
{
group = req.body.group;
}
it is unclear exactly what you mean by "node js stop everything". If you want to stop the entire nodejs process, you can use process.exit().
If you're trying to keep some code after your error from executing and your error occurs in an async callback, then you can't do that. The other code has already executed.
If you want to serialize asynchronous operations so that you complete one async operation BEFORE you decide whether to start the next operation, then you will need to code that differently. You will need to execute the second block of code from within the completion callback of the first async operation.
One aspect of your code that you may not understand is that this block is asynchronous:
User.find({_id:uid}).exec(function(err, data)
{
if(err){
res.status(404);
res.json('user not found');
res.end();
return;
}
});
The callback you pass to .exec() is called sometime LATER. Meanwhile, the rest of your JS has already executed. In addition, when you do a return from within that callback that doesn't return from your outer function, it only returns from that callback function back into the bowels of .exec(). It stops any more of the callback from executing, but has no effect at all on the outer function because that outer function.
So, if you want the .find() operation to finish before you execute the rest of your code in that function, you have to put that code inside the callback function.
var userLat = db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
console.log(docs.currentUserLat);
})
});
This is my code, I'm trying to get the value that's console logged into the variable. I just can't find the correct syntax to do this. The console log does return the correct value just need to drop it into the variable. Grateful for some help.
What do you want to do with 'docs.currentUserLat'?
You can do what you need to do without saving docs.currentUserLat to a variable that has scope outside of your db.collection call. Some examples:
If you simply want to change the document in your database, take advantage of the many methods specified in the Collections API: http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html. For example, to update the document and simultaneously resave it in the database:
db.collection('users', function (err, document){
document.findOneAndUpdate({_id: loggedUserID},
{currentUserLat: [updated value]},
function(err, docs) {
if(err) console.log(err);
}
)
});
If you just wanted to use docs.currentUserLat inside some node function, you'll need to properly nest the document.findOne function inside a callback (or vice versa). For example, to write currentUserLat to a file using the fs module:
var fs = require('fs');
db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
fs.writeFile("pathToYourFile", docs.currentUserLat, function(err) {
if(err) {return console.log(err);}
});
});
});
Or, if you want to send it in response to a simple http request:
var http = require('http');
http.createServer(function(request,response){
db.collection('users', function (err, document){
document.findOne({_id: loggedUserID}, function(err, docs) {
response.writeHead(200,{'Content-Type':'text/html'});
response.end(docs.currentUserLat);
});
});
});
The key thing to remember is what JohnnyHK said in their comment: docs.currentUserLat is only available inside the anonymous function passed to findOne. So, whatever it is that you need to do, do it inside this function.
(Reading the link JohnnyHK provided is a great way to get started with understanding asynchronous functions in Node. Another is https://github.com/rvagg/learnyounode)
First of all you have to understand how javascript callback works. After that you will see that nothing assigns docs.currentUserLat to your userLat variable. The reason behind this is that your docs.currentUserLat is available only inside the callback. Think about it in the following way:
You program started to execute and encountered the line: var userLat = .... This line tells: do a callback (which basically asks someone else to do the job), your while your job is being executed the program continues, by assigning userLat to undefined and executes further. Then at some period of time callback finishes and console.log your docs.currentUserLat.
One way to have the desired behavior is to make userLat global and instead of console.log(docs.currentUserLat); do userLat = docs.currentUserLat. The problem that if you will do this, your userLat eventually will have the desired value (if callback will not fail), but you can not predict when. So if you will do
var userLat = db.collection('users', function (err, document){ ... });
.. some other code
console.log(userLat);
you will not be sure that you will get the output. Another way to do put everything in another callback.