I was experimenting with Promise in nodejs to better understand how it works. I have partially working code, but I am not sure if I am using it correctly. My intention is to pass error or correct result back to caller function. This seem to get complicated with all these callbacks. Here is what I have so far.
//This is my main CALLER function
function query_mcafee_mssql_type(database, node_name, type) {
return new Promise(function (fulfill, reject) {
switch (type) {
case "currentDefinitionDate":
computeCurrentDefinitionResult(database, node_name)
.then(function (result) {
console.log('query_mcafee_mssql_type' + result);
fulfill(result);
});
break;
}
});
}
function computeCurrentDefinitionResult(database, node_name) {
return new Promise(function (fulfill, reject) {
var leaf_sql_query = "SELECT * FROM "+ JSON.stringify(database) +".dbo.EPOLeafNode WHERE NodeName=" + "'" + node_name + "'";
query_mcafee_mssql(leaf_sql_query)
.then(function (LeafNode) {
if (LeafNode == undefined) {
fulfill(LeafNode);
} else {
return LeafNode;
}
})
.then(function (LeafNode) {
console.log('computeCurrentDefinitionResult' + LeafNode);
var product_properties_sql_query = "SELECT * FROM "+ JSON.stringify(database) +".dbo.EPOProductProperties WHERE ParentID=" + "'" + LeafNode.AutoID + "'" + "AND ProductCode LIKE 'VIRUSCAN%'";
return query_mcafee_mssql(product_properties_sql_query);
})
.then(function (ProductProperty) {
if (ProductProperty == undefined) {
fulfill(ProductProperty);
} else {
return ProductProperty;
}
})
.then(function (ProductProperty) {
fulfill(ProductProperty.DATDate);
});
});
}
function query_mcafee_mssql(sql_string) {
return new Promise(function (fulfill, reject) {
query_mssql(mcafee_config, sql_string)
.then(function (sql_response) {
fulfill(sql_response);
console.log('query_mcafee' + sql_response);
});
});
}
function query_mssql(config, sql_string){
return new Promise(function (fulfill, reject) {
var connection = new sql.Connection(config, function(err) {
// ... error checks
if (err) {
console.log('connection to mssql has failed');
//throw err;
fulfill();
} else {
// Query
var request = new sql.Request(connection);
request.query(sql_string, function(err, recordset) {
// ... error checks should go here :
if (err) {
console.log('requst query error');
fulfill();
} else {
// output query result to console:
//console.log(recordset);
fulfill(recordset);
}
});
}
});
});
}
My main caller function is query_mcafee_mssql_type(). I use Promise to allow the execution of the query. Once that is done, if its an error, I would like "undefined" to returned else the correct result to returned to the caller.
As per my understanding, fulfill and reject callbacks decide the fate of Promise.
The top most in call stack is function query_mssql(). My assumption was that once I call "fulfill" with result if success or return fulfill() empty if error.
The function above that is query_mcafee_mssql() which is oblivious to error or success and just passes on the result.
The function computeCurrentDefinitionResult() is where all the problem arises. I need to make two sql query one after the other. However if first query fails then I don't see any point in proceeding with next query that means
query_mcafee_mssql(leaf_sql_query)
.then(function (LeafNode) {
if (LeafNode == undefined) {
fulfill(LeafNode);
} else {
return LeafNode;
}
})
I do not want rest of the .then to be executed as it does not make sense if LeafNode is undefined. I want to return the LeafNode value back to its caller. However if I return fulfill(), the code flow seem to move to next .then. If I use, reject(), the caller query_mcafee_mssql_type() .then block is not getting called. The relevant block is show below.
computeCurrentDefinitionResult(database, node_name)
.then(function (result) {
console.log('query_mcafee_mssql_type' + result);
fulfill(result);
});
- How can I return the actual result from computeCurrentDefinitionResult() ??
- Does all the functions need to return "Promise" to achieve what I am doing?
- Why does the code patch not return from the function after "fulfill()" is called?
- Is there a need to use "return" in these function blocks?
Any help would be appreciated. Thanks.
From your code, I got a feeling that you have no idea about the correct usage of resolve and reject in Promise. Here is the right one.
function query_mssql(config, sql_string){
return new Promise(function (resolve, reject) {
var connection = new sql.Connection(config, function(err) {
// ... error checks
if (err) {
return reject(err);
}
// Query
var request = new sql.Request(connection);
request.query(sql_string, function(err, recordset) {
// ... error checks should go here :
if (err) {
return reject(er);
}
// output query result to console:
//console.log(recordset);
resolve(recordset);
});
});
});
}
//Then use it like this
query_mssql(config,sql_string)
.then(function(LeafNode){
//query success
console.log(LeafNode);
}).catch(function(er){
//query failed,
console.log(er);
});
Related
Hopefully someone can point me to the right direction. I read up on waiting for functions to complete before continuing and I resolved myself to using await/async but I am just stuck now.
I tried to get the Async/Await process to work, tried to inject the await in various locations, with adjusting the functions to be async, but i can not get the PSA_Resultbody to return to the original request. Any pointers would be appreciated.
Thank you,
CE
PSA_Resultbody = ProcessPSAAPI(xmlpackage, PSA_Action);
console.log("3 - Returned data:" + PSA_Resultbody);
calls the below:
async function ProcessPSAAPI(xmlpackage, PSA_Action) { //psa action is part of the options
var options = {...};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log('0 - Start '+body.toString());
if(res.statusCode != 200) {
PSA_Resultcode = "Error: " +res.statusCode +" - "+ res.statusMessage;
} else {
PSA_Resultcode = "Success: " +res.statusCode +" - "+ res.statusMessage;
PSA_Resultbody = ParseResults(body.toString()); //parse the results for later use --SCRIPT NEEDS TO WAIT FOR RESULTBODY TO COMPLETE
console.log("1 -PSA_Resultbody as part of RES = "+PSA_Resultbody);
}
});
res.on("error", function (error) {
console.error(error);
PSA_Resultcode = res.statusCode +" - "+ res.statusMessage;
});
});
console.log('2 -RESULT BODY BEFORE SENDING BACK TO INITIATING FUNCTION: '+PSA_Resultbody);
req.write(xmlpackage);
req.end();
return PSA_Resultbody;
Based on the above, my console log order is: 3,2,0,1 in stead of 0,1,2,3.
0 and 1 will have the correct data, so the API Call does work, but 2 will be "undefined" and should have the same data that is in 1.
There's no way to await an event emitter, so using async in this case isn't going to be useful. You cannot "return" from inside an event either.
The solution here is to return a new custom promise and to use resolve() inside of the "end" event of your emitter.
It will look something like this:
function ProcessPSAAPI(xmlpackage, PSA_Action) {
return new Promise( (resolve, reject) => {
// other code
res.on("end", function (chunk) {
// other code
resolve(PSA_Resultbody);
});
res.on("error", function (error) {
// other code
reject(error);
});
});
}
Here's a quick tutorial on creating your own promises, which I've written to simplify comprehension of the subject (official docs are somewhat dry and complex imho).
I did not change your code. I just put the appropriate promise structure in to get you started. This should really be a lesson in promises. async await is a shorthand promise structure. A Promise is one way you wait on code. It can be thought of as an array of callbacks that will be executed when the Promise is resolved.
A simple promise works like this:
const myPromise = new Promise(function(resolve, reject) {
/* Your logic goes in here. It can be anything.
* But the important part to remember is that when you have success, resolve it.
* When you have a failure, reject it.
*/
someCallBackPattern(function(error, data) {
if(error) {
reject(error);
} else {
resolve(data);
}
});
});
// To get the data out you use 'then', and 'catch'. then has two arguments.
myPromise.then(function(data) {
// The first argument is the result from resolve.
}, function(err) {
// The second argument is the result from reject.
}).catch((err) => {
// you can also get to the error from the catch callback
});
This is kinda messy and complex. So there is async await.
async function() {
try {
const result = await myFunctionThatReturnsAPromise();
// result is the resolved data
} catch (err) {
// err is the rejected Error
}
}
function myFunctionThatReturnsAPromise() {
return new Promise((resolve, reject) => {
// your code
})
}
And thats how it works.
async function someFunction () { // You can not wait on results unless you are in an await function
PSA_Resultbody = await ProcessPSAAPI(xmlpackage, PSA_Action); // await on your results.
console.log("3 - Returned data:" + PSA_Resultbody);
}
function ProcessPSAAPI(xmlpackage, PSA_Action) { // This does not need to be async. Unless you are awaiting in it.
return new Promise((resolve, reject) => { // async await is a shorthand promise structure. Although you do not need to use promises. It really helps to get the structure correct.
var options = {...};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log('0 - Start '+body.toString());
if(res.statusCode != 200) {
PSA_Resultcode = "Error: " +res.statusCode +" - "+ res.statusMessage;
reject(new Error(PSA_Resultcode)); // Reject you errors
} else {
PSA_Resultcode = "Success: " +res.statusCode +" - "+ res.statusMessage;
PSA_Resultbody = ParseResults(body.toString()); //parse the results for later use --SCRIPT NEEDS TO WAIT FOR RESULTBODY TO COMPLETE
console.log("1 -PSA_Resultbody as part of RES = "+PSA_Resultbody);
resolve(PSA_Resultbody); // Resolve your result
}
});
res.on("error", function (error) {
console.error(error);
PSA_Resultcode = res.statusCode +" - "+ res.statusMessage;
reject(new Error(PSA_Resultcode)); // Reject you errors
});
});
console.log('2 -RESULT BODY BEFORE SENDING BACK TO INITIATING FUNCTION: '+PSA_Resultbody);
req.write(xmlpackage);
req.end();
})
}
I have an issue where I may need to jump out of the whole promise chain because of some value or take two paths based on a value.
How best do you do that?
Here is the first scenario where I would like to just jump out of the whole chain. I just want to give them a message.
DB_WorkIssues.info().then(function (details) {
if (details.doc_count == 0 && details.update_seq == 0) {
showMsg("This device has no local data on it and no access to the Server, please come back when you are online.")
} jump out here, no need to do the next set.
else
return; Continue on as the values are valid.
}).then(function () {
return ajaxCallForJson(URI_LookupTables);
}).then(function (json) {
return callBulkDocLoad(DB_LookupTables, json);
}).then(function () {
return loadCategoriesDDL();
}).then(function () {
return loadEquipmentDDL();
}).catch(function (err) {
showMsg("Error in defineDBs: " + err);
});
In the 2nd scenario, I may want to take one path if the values are one thing and another if the values are another. But I still want the chains to work with the first promise. Something like this:
DB_WorkIssues.info().then(function (details) {
if (details.doc_count == 0 && details.update_seq == 0) {
Take this path.
return;
}).then(function () {
return ajaxCallForJson(URI_LookupTables);
}).then(function (json) {
return callBulkDocLoad(DB_LookupTables, json);
}).catch(function (err) {
showMsg("Error in defineDBs: " + err);
});
}
else
{
Take this path instead
return;
}).then(function () {
return loadCategoriesDDL();
}).then(function () {
return loadEquipmentDDL();
}).catch(function (err) {
showMsg("Error in defineDBs: " + err);
});
}
Thanks.
Here is what I was thinking after looking at the answer where I do the second promise always and only do the first in some cases.
DB_WorkIssues.info().then(function(details) {
// promise variable , defined in conditional
var promise;
Would I set the promise to some default value, in case the following test fails
if (details.doc_count == 0 && details.update_seq == 0) {
// return this promise
promise = ajaxCallForJson(URI_LookupTables).then(function(json) {
return callBulkDocLoad(DB_LookupTables, json);
});
}
return promise;
}).then(function () {
return loadCategoriesDDL();
}).then(function () {
return loadEquipmentDDL();
}).then(function () {
return loadLocationsDDL();
}).catch(function (err) {
showMsg("Error in defineDBs: " + err);
});
Is that how I could do it?
Thanks.
I think this is a skeleton that represents what you're going for. Promises are incredibly powerful and worth studying. I tried to add helpful comments but I suggest playing around with the code and understanding what's going on.
// Six named promise-creators. When called with (x), will create a promise
// which waits 200ms and then logs and resolves with (x).
// These could represent any asynchronous operation.
const p1 = p2 = p3 = p4 = p5 = p6 =
(x) => {
const p = new Promise((resolve, reject) => {
setTimeout(() => {resolve(x); console.log(x)}, 200)
});
return p;
}
// A function which, when called, will execute first promise chain.
const first_steps = () =>
p1(1)
.then(result => p2(2))
.then(result => p3(3))
// A function which, when called, will execute second promise chain.
const second_steps = () =>
p4(4)
.then(result => p5(5))
.then(result => p6(6))
// When true, this prints numbers 1-6.
// When false, only prints numbers 4-6.
if (false) {
console.log(first_steps().then(second_steps));
} else {
second_steps();
}
Seems to me you have extra sets of then() and your if() would determine which promise to return something like:
DB_WorkIssues.info().then(function(details) {
// promise variable , defined in conditional
var promise;
if (details.doc_count == 0 && details.update_seq == 0) {
// return this promise
promise = ajaxCallForJson(URI_LookupTables).then(function(json) {
return callBulkDocLoad(DB_LookupTables, json);
});
} else {
// or this promise
promise = loadCategoriesDDL().then(function() {
return loadEquipmentDDL();
});
}
promise.catch(function(err) {
showMsg("Error in defineDBs: " + err);
});
return promise;
})
I have the following nodeJS code.I need to get the machines for each service from the redis database. I am using the 'q' library to simplify the callback problem. However I do not get the output.
I am new to node/callbacks/q. Where is my mistake in the code?
I have a controller.js file with the following code
function getMachines(services) {
var machines = Q.fcall(function() {});
services.forEach(function(service) {
var value = function() {
var deferred = Q.defer();
redisDB.readfromRedis(service, function(result) {
deferred.resolve(result);
});
return deferred.promise;
}
});
return machines;
}
testController.js(calling the getMachines function from the controller.js )
var services = ['dashDb22', 'service1', 'service2', 'service3']
var output = controller.getMachines(services)
console.log(output);
RedisDb.js
function readfromRedis(key, callback) {
client.smembers(key, function(error, value) {
if (error) {
throw error;
}
console.log('VALUE IS: = ' + value);
callback(value);
});
}
Your getMachines() doesn't do much, machines is useless and inside your forEach(), you're storing a function you never execute. Your code being simple, you don't really need to use Q, nodejs has a native Promise support.
function getMachines(services) {
// create an array of promises
var myPromises = services.map(function (service) {
// for each service, create a Promise
return new Promise(function (resolve, reject) {
redisDB.readfromRedis(service, function (result) {
resolve(result);
});
});
})
// takes an array of promises and returns a promise for when they've all
// fulfilled (completed successfully) with the values as the result
return Promise.all(myPromises);
}
getMachines(services).then(function (machines) {
// use machines here
});
You could also make readfromRedis() a promise to make it simpler to use.
I need to keep calling remote API until I get the response I need, and I would like to use the Official A+ promises in node.js. Sync psudo code:
params = { remote api call params }
while (true) {
result = callRemoteApi(params)
if isGood(result) {
onSuccess(result)
break
}
params = modify(params)
}
I am using the request-promise lib for requests, so the result might be something like this:
new Promise(function (resolve, reject) {
var task = request({params})
.then(function (result) {
if (isGood(result)) {
resolve(result);
} else {
task = request({new params}).then(this_function);
}
});
P.S. This is very similar to https://stackoverflow.com/a/17238793/177275, but I would like a non-q-based implementation.
Something like this should work well:
var remoteApiCall = function(num){
// fake function to resolve promise if random number in range
return new Promise(function(resolve, reject){
return ((Math.random()*10) < num)
? resolve(true)
: reject(false);
})
}
function getUntil(num){
return remoteApiCall(num).then(function(result){
if (result) {
return result
} else {
// call again until you get valid response
return getUntil(num)
}
})
}
getUntil(num).then(...)
The following solution addresses a few specific problems:
how to break the (otherwise endless) loop
how to access the result of a previously failed attempt
how to incorporate your params = modify(params)
The problem can be broken down into two sections:
a repeater/promise runner (that itself returns a promise so we can hook onSuccess to it)
a promise provider - a function that creates promises for the repeater
The repeater would look like this:
function repeatUntilSuccess(promiseProvider) {
return new Promise(function (resolve, reject) {
var counter = 0;
function run(failedResult) {
var p = promiseProvider(failedResult, counter++);
if (p instanceof Promise) {
p.then(resolve).catch(run);
} else {
reject(p);
}
}
run();
});
}
The "loop" happens in this line: p.then(resolve).catch(run);. The repeater keeps calling the promise provider until the promise it returns resolves (in which case the repeater resolves) or until it no longer provides a promise (in which case the repeater rejects).
A promise provider can be any function(previousResult, counter) that returns a promise (or not, if you wish to stop the loop).
var params = {/* ... */};
function nextRequest(previousResult, counter) {
if (counter >= 10) return "too many attempts";
if (previousResult) params = modify(params);
return apiRequest(params);
}
(This setup assumes that apiRequest() returns a promise)
Now you can do this:
repeatUntilSuccess(nextRequest).then(onSuccess).catch(onError);
Since your question includes the side-task of wrapping an HTTP request in a promise:
function apiRequest(params) {
return new Promise(function (resolve, reject) {
return request(params).then(function (result) {
if (isGood(result)) {
resolve(result);
} else {
reject(result);
}
});
});
}
Open the browser console and run the following snippet to see it in action.
function repeatUntilSuccess(promiseProvider) {
return new Promise(function (resolve, reject) {
var counter = 0;
function run(failedResult) {
var p = promiseProvider(failedResult, counter++);
if (p instanceof Promise) {
p.then(resolve).catch(run);
} else {
reject(p);
}
}
run();
});
}
// mockup promise that resoves or rejects randomly after a timeout
function randomPromise(num){
return new Promise(function(resolve, reject){
setTimeout(function () {
var randomNum = Math.floor(Math.random() * num * 10);
if (randomNum < num) {
resolve(randomNum);
} else {
reject(randomNum);
}
}, 300);
});
}
// promise provider
function nextPromise(prev, i) {
if (prev) console.info("failed attempt #" + i + ": " + prev);
if (i >= 5) return "too many attempts:" + i;
return randomPromise(100);
}
// run it!
repeatUntilSuccess(nextPromise).then(function (result) {
console.log("success", result);
}).catch(function (result) {
console.log("failed", result);
});
I have a function that does some operation using an array.
I would like to reject it when the array is empty.
As an example
myArrayFunction(){
return new Promise(function (resolve, reject) {
var a = new Array();
//some operation with a
if(a.length > 0){
resolve(a);
}else{
reject('Not found');
}
};
}
When the reject operation happens I get the following error.
Possibly unhandled Error: Not found
However I have the following catch when the call to myArrayFunction() is made.
handlers.getArray = function (request, reply) {
myArrayFunction().then(
function (a) {
reply(a);
}).catch(reply(hapi.error.notFound('No array')));
};
What would be the correct way to reject the promise, catch the rejection and respond to the client?
Thank you.
.catch takes a function as parameter however, you are passing it something else. When you don't pass a function to catch, it will silently just fail to do anything. Stupid but that's what ES6 promises do.
Because the .catch is not doing anything, the rejection becomes unhandled and is reported to you.
Fix is to pass a function to .catch:
handlers.getArray = function (request, reply) {
myArrayFunction().then(function (a) {
reply(a);
}).catch(function(e) {
reply(hapi.error.notFound('No array')));
});
};
Because you are using a catch all, the error isn't necessarily a No array error. I suggest you do this instead:
function myArrayFunction() {
// new Promise anti-pattern here but the answer is too long already...
return new Promise(function (resolve, reject) {
var a = new Array();
//some operation with a
if (a.length > 0) {
resolve(a);
} else {
reject(hapi.error.notFound('No array'));
}
};
}
}
function NotFoundError(e) {
return e.statusCode === 404;
}
handlers.getArray = function (request, reply) {
myArrayFunction().then(function (a) {
reply(a);
}).catch(NotFoundError, function(e) {
reply(e);
});
};
Which can be further shortened to:
handlers.getArray = function (request, reply) {
myArrayFunction().then(reply).catch(NotFoundError, reply);
};
Also note the difference between:
// Calls the method catch, with the function reply as an argument
.catch(reply)
And
// Calls the function reply, then passes the result of calling reply
// to the method .catch, NOT what you wanted.
.catch(reply(...))