I'm trying to figure out how to not get undefined at "hhh" myresult. Whats happening is jjj comes back as hello, then I get undefined for myresult, then "here" comes back with all the api data afterwards. It seems like the api is taking too long to return so it gives me undefined. I cannot use the data to update my form while this happens. Any suggestions?
var myresult;
soap.createClient(url, function(err, client) {
client.getShipQuote(args, function(err, result) {
if (err) {
console.log(err.message);
}
console.log('RESPONSE BODY:');
console.log("here", result.return.shipQuote[0].chargesCollection);
myresult = result.return.shipQuote[0].chargesCollection;
});
});
var x="hello";
bluebird.all([x, myresult])
.spread(function(x, myresult){
console.log("jjj", x);
console.log("hhh", myresult);
response.send(x);
});
First off, Bluebird is a "promise" library. That means it creates and operates on promises. To use it with an asynchronous operation, you have to have an asynchronous operation that returns a promise, not one that takes a regular callback. Typically, if the async operation you are starting with doesn't return a promise, then you can "promisify" it by making a new version of it that uses the callback it supports to drive a promise. Then, you can use all of Bluebird's features with the new promisified version of that function.
Bluebird, by itself, does not have any magic powers to know when an asynchronous operation is done. And, it has no powers to make an asynchronous operation appear to be synchronous. It supplies tools for managing and coordinating asynchronous operations so you can know when they are done, when results are available and if/when there are errors.
It appears that you have two asynchronous operations, one inside the other soap.createClient()
and inside that client.getShipQuote()
. And, it looks like you want to sequence them so that the first is called, that result is passed to the second and then in some other code, you can use the final result. Here's how I would recommend doing that using promises:
const Promise = require('bluebird');
// make promisified versions of soap methods
// only have to do this once
Promise.promisifyAll(soap);
// call promisified version
soap.createClientAsync(url).then(client => {
// could perhaps do this only once beforehand if you know how to reach the client.prototype object
// before a client object is created - requires knowing a little about how the library is structured
Promise.promisifyAll(client);
return client.getShipQuoteAsync(args);
}).then(result => {
// handle final result here
response.send(...);
}).catch(err => {
// handle error here - this will catch all prior errors
console.log(err);
response.status(500).send(...);
});
If you wanted to do this with just normal callback programming (no promises), then you'd do something like this:
soap.createClient(url, function(err, client) {
if (err) {
console.log(err.message);
// send error response
response.status(500).send(...);
} else {
client.getShipQuote(args, function(err, result) {
if (err) {
console.log(err.message);
// send error response
response.status(500).send(...);
} else {
let myResult = result.return.shipQuote[0].chargesCollection;
// send response here
response.send(...);
}
}
}
});
Note that in all cases with asynchronous programming, you have to use the result inside the callback in which the result is made available (or in a function you call from there). You cannot just stuff the result into a higher scoped variable and use it in code outside that callback because your timing will not be correct for when that value is available. That is why you were getting undefined
in your code because your code was examining the variable BEFORE the async operation had finished and before the value was set. Only in the completion callback can you actually know the value is available. Promises don't change that fact. They just make it easier for you to manage and coordinate the callbacks.