I'm using the node-soap client from milewise (create API by the way), but I have some difficulties to get the results of the callback to the right scope.
Here is the code I have for now:
function generateSoapRequest(req, res, next)
{
soap.createClient('http://127.0.0.1:' + cfg.service_port + cfg.service_url_path,
function(err, client) {
client.CXIf.CXIf.CXProcessXML(
{"XMLRequestData": {"CXLogon": {"UserID":"1901007", "Password":"2580", "LogonType":11 } } },
function(err, result, body) {
if (err) {
console.log(err);
return;
}
console.log(result);
var cxresponse = result.XMLResponse[0].CXResponse;
console.log('SessionID:' + cxresponse.SessionID + ' SessionInstanceID:' + cxresponse.SessionInstanceID);
});
});
}
function getVoiceMailInformation(req, res, next) {
var cxresponse = generateSoapRequest(req, res, next);
var something = doSomethingNext(cxresponse);
}
function doSomethingNext(cxresponse){....; return something;}
Basically, when I launch the getVoiceMailInformation(), it creates a soap client and request some information through the generateSoapRequest().
The next step would be to get the result of that function (not implemented in the code above, because I don't know how) and do something else.
My problem is soap.createClient is asynchronous, so the callback is fired well after the function is complete.
What would be the best approach ?
(Maybe it's something trivial, but the scope of an anonymous function in javascript is something that is killing me.)
Any help will be very appreciated.
Basically you can't do something like:
var cxresponse = generateSoapRequest(req, res, next);
because the function you're calling invokes asynchronous code, and therefore can't return a value that's determined by that code. The normal way around this is to give the function an extra callback parameter for a function that will be called with the result once the result becomes available. It doesn't have to be an anonymous function; it can be a named function. In your case, (assuming you've modified generateSoapRequest to take a callback as its fourth argument and call it when the results are ready, you could write
generateSoapRequest(req, res, next, doSomethingNext);
and then doSomethingNext will be called with cxresponse as an argument. Of course, since doSomethingNext also gets called asynchronously, it can't return a value either, so you'll have to apply the same technique to it.
The async module can make this sort of thing easier: in particular, its "waterfall" pattern is useful when you have a bunch of functions that have to run in sequence, each being called back from the previous one.
Related
I am new learning to create rest API's using Node, Express and MySQL. I have my function removeAll that takes no parameters, it passes the result object into an arrow function which then runs a sql function which then takes another function as a parameter (err, res). Inside of the nested arrow function if we do not have any errors and the affected rows does not equal 0 we get to the result which returns (null, res). Now my confusion is as to why result contains (null, res) rather than just res. I am also confused as to how res is actually being assigned, I see that it is being passed into the arrow function but we dont explicitly set what res is.
sql.query("DELETE FROM customers", (err, res) => {
if (err) {
console.log("error: ", err);
result(null, err);
return;
}
if (res.affectedRows == 0) {
// customer not found with the id
result({ kind: "not_found" }, null);
return;
}
console.log("deleted customer with id: ", id) {
result(null, res);
}
});
};
result is a node-style callback, where the first parameter is the error (if any; if no error, first parameter is null), and the second parameter is the result (if no error is encountered).
The general approach is very similar to the sql.query callback you're using:
sql.query("DELETE FROM customers", (err, res) => {
Just like how its callback defines the error parameter first, and the result second, you similarly want to call result with the error as the first parameter, or else with the result as the second parameter.
That said, result is a pretty confusing variable name - it's a callback, not a result which contains data, so you might want to rename it to something more appropriate, so as to reduce potential confusion.
I am also confused as to how res is actually being assigned
It's handled by the internals of sql.query. When the query resolves successfully, it will do something like callback(null, results). When the query fails, it will do something like callback(someError). callback is the callback that you pass to sql.query.
Now my confusion is as to why result contains (null, res) rather than just res.
That's the standard way old-style Node.js callbacks work: The first argument is an error or null, and if the first argument isn't an error, the second argument contains the data "returned" by the call.
These days, you'd use a promise, but that's how the old-style callbacks work.
I am also confused as to how res is actually being assigned, I see that it is being passed into the arrow function but we dont explicitly set what res is.
The sql.query function is what calls the callback you pass into it. That's where res comes from, it's the result of the database operation. sql.query also follows the standard old-style Node.js callback pattern.
I know that Array.forEach is synchronous.
But I've this code:
function middleware (req, res, next) {
Array.forEach(
Array.forEach(
if(true) { return next(); }
)
)
return res.send("false");
}
// next()
function isTrue (req, res) {
return res.send("true");
}
And every time I execute it, it sends two headers. First the isTrue function header, and later, the middleware function header.
I understand that if Array.forEach() is synchronous, it should be executed before res.send("false"). But it's not the behaviour. So there is something that I don't understand.
To answer the complete question we'd need to see your code, but to answer the very basic javascript question, the return inside forEach only returns from forEach and not from your outer middleware function. So everything within the middleware function is still executing after forEach.
Again, it's impossible to offer a real solution, but instead of forEach you'd typically use some for these kind of checks.
const ret = [1,2,3].some(e => e === '<yourCondition>');
res.send(ret);
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.
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.
I am learning Node.js, and I have read some tutorials, like The Node Beginner Book for learning the core funcionality. But the more I read some examples, the more doubts I start collecting.
On the further example, obtained from a tutorial, we can see that for a CRUD 'read' request for key /documents/titles.json, we are returning a value:
app.get('/documents/titles.json', loadUser, function(req, res) {
Document.find({ user_id: req.currentUser.id },[], { sort: ['title', 'descending'] },
function(err, documents) {
res.send(documents.map(function(d) {
return { title: d.title, id: d._id };
}));
});
});
On this example, the function loaduser() is used for authentication purposes:
function loadUser(req, res, next) {
if (req.session.user_id) {
User.findById(req.session.user_id, function(err, user) {
if (user) {
req.currentUser = user;
next();
} else {
res.redirect('/sessions/new');
}
});
}
}
What I don't understand is:
I suppose that node.js, before start executing the app.get, it goes to loaduser function. loadUser() function has three parameters: req,res,next, but I don't see, at least, how you pass from app.get() the "req" parameter to loadUser(). From where does it come?
Inside loadUser() function, when you execute next(), it means that the function app.get()" can continue its procedure, but this req.currentUser = user, is the same req that is used on app.get() function?
Inside loadUser() function, when you execute res.redirect() code, automatically breaks the procedure on app.get() function, right? it looks like it doesn't return to Document.find().
The questions you've asked are about the Express framework internals specifically:
When you call app.get(route, loadUser, final) Express will make a stack (array) with the loadUser and final function functions and it will know that when you call next it should execute the following function in the stack with the same req and res params.
When you call next it will just pass to the next function in the middleware stack.
Since you call res.redirect and you don't call return, it won't pass to the next function in the stack (the one with Document.find).
Resources:
http://howtonode.org/getting-started-with-express
I think in order to be comfortable with this you need get familiar with idea of middleware and how it's used in connect framework.
I've found few articles where this subject explained well enough. Take a look there:
http://howtonode.org/connect-it
and here http://stephensugden.com/middleware_guide/
the main idea is you have a set of layers and each time when new request arrives it goes through each level and on each level you can decide what to do with that. You can stop at some level, do something and return response or you can pass it to the next layer