While loop with A+ promises in JavaScript / node.js - javascript

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);
});

Related

implementing bare minimum promise function to understand other promise frameworks

I have been trying to understand the architecture behind a promise call in javscript and here is something that i assumed is happening behind the scene
function Promise() {
this.stack = [];
this.then = function(fn) {
this.stack.push(fn);
return this;
}
this.resolve = function(data) {
if (this.stack.length) {
var cb = this.stack[0];
this.stack.shift();
cb.call(null, {
resolve: this.resolve.bind(this)
}, data);
}
}
}
// --- below is a working implementation --- //
var bar = function() {
var promise = new Promise();
setTimeout(function() {
console.log("1");
promise.resolve();
}, 2000);
return promise;
}
bar().then(function(p) {
setTimeout(function() {
console.log("2");
p.resolve();
}, 1000);
}).then(function(p) {
setTimeout(function() {
console.log("3");
p.resolve();
}, 500);
}).then(function(p) {
setTimeout(function() {
console.log("4");
p.resolve();
}, 300);
});
in my library, after every resolve call, i am calling the next callback in the stack and shifting the array by one
however in other libraries that i have linked, every-time the first promise is resolved a loop is being run through out the entire array while it keeps calling the callback stacks.
D.js
function execCallbacks() {
/*jshint bitwise:false*/
if (status === 0) {
return;
}
var cbs = pendings,
i = 0,
l = cbs.length,
cbIndex = ~status ? 0 : 1,
cb;
pendings = [];
for (; i < l; i++) {
(cb = cbs[i][cbIndex]) && cb(value);
}
}
tiny Promise.js
_complete: function(which, arg) {
// switch over to sync then()
this.then = which === 'resolve' ?
function(resolve, reject) {
resolve && resolve(arg);
} :
function(resolve, reject) {
reject && reject(arg);
};
// disallow multiple calls to resolve or reject
this.resolve = this.reject =
function() {
throw new Error('Promise already completed.');
};
// complete all waiting (async) then()s
var aThen, i = 0;
while (aThen = this._thens[i++]) {
aThen[which] && aThen[which](arg);
}
delete this._thens;
}
tiny closure Promise.js
function complete(type, result) {
promise.then = type === 'reject' ?
function(resolve, reject) {
reject(result);
} :
function(resolve) {
resolve(result);
};
promise.resolve = promise.reject = function() {
throw new Error("Promise already completed");
};
var i = 0,
cb;
while (cb = callbacks[i++]) {
cb[type] && cb[type](result);
}
callbacks = null;
}
I want to understand, why is there a loop being run through the array to handle the next function chained to the resolve !
what am i missing in my architecture?
in my library, after every resolve call, i am calling the next callback in the stack and shifting the array by one
That's a callback queue, not a promise. A promise can be resolved only once. When making a .then() chain of promises, it simply creates multiple promise objects, each one representing the asynchronous result after a step.
You might want to have a look at the core features of promises.

Pattern for dynamic Javascript promises

Inside a promise, I need to call and process an indeterminate number of asynch API responses after individually calling them either inside another promise, or after said promise, but before another so the order of execution is respected.
var promiseA = function() {
return new Promise(function(resolve, reject) {
// 1. Establish objects needed from one API endpoint
// 2. Call API endpoint for each object and parse
// 3. Only then continue to next promise
}
}
var finalPromise = function() {
return new Promise(function(resolve, reject) {
//
}
}
promiseA()
.then(finalPromise)
So inside promiseA, I find out how many objects I'll need to poll individually from an API. Each request is of course asynchronous. I need to make these calls and process the response before the final promise is called.
I am struggling to determine a pattern for this with promises, where I can dynamically create these promises and only allow the final promise to execute after the indeterminate and asynchronous have executed and processed. I've worked with other languages where this is possible, but I'm struggling to see it here with Promises.
Any help is appreciated.
I have changed the answer to incorporate the comments below. Since, you mentioned ES6 promises I shall stick to that. There are two basic types of callbacks that we might care about.
DOM load or other one time event callbacks (window.onload and so on)
Async method callback (AJAX call, setTimout and so on)
Since,
1.DOM load or other one time event
var p = new Promise(function(res, rej) {
window.onload = res();
};
2.Plain callback: these are callbacks that don't conform to a convention. e.g. setTimeout
var p = new Promise(function(res, rej){
setTimeout(function() {
//your business/view logic
success? res():rej(); //if successful resolve else reject
}, 2000);
});
In each of the above case the promise (var p) can be wrapped to be returned by a function.
var myAsyncMethod = function () {
var p = new ... // as mentioned in 1 or 2
return p;
}
Then the usage:
myAsyncMethod()
.then(function(){/* success-handler */})
.catch(function(/* failure-handler */));
Specific to your question you may have many such methods:
function baseAJAXCall (url) {
new Promise(functoin(rej, res) {
$.get(url, function(err, data){
if(err) {
rej();
}
else {
resolve(data);
}
});
}
};
function callAPIEndpoint(url) {
return baseAJAXCall(url);
}
function finalPromiseHandler () {
//your final business/view logic
}
//USAGE
callAPIEndpoint('/my-first-call')
.then(function(data){
var promiseArray = data.map(function(item){
return baseAJAXCall(item.url);
});
return Promise.all(promiseArray);
})
.then(finalPromiseHandler)
.catch(function(){
console.log('.error-message.');
});
Ref:
How do I convert an existing callback API to promises?.
http://www.datchley.name/es6-promises/
Links from comments below.
---OLD ANSWER: PLEASE OVERLOOK---
I am familiar with this library : https://github.com/kriskowal/q. And, you can do this using using the q.all and q.allSettled constructs. May be that is what you are looking for.
Normally, the pattern is to create a function that returns a promise.
function someAsyncFuncName1(url) {
var def = q.defer();
//async function
$.get(url, function(err, data){ //suppose
if(err){
def.reject();
}
else {
def.resolve(data); //pass the data to the .then() handler.
}
});
return def.promise;
}
function someAsyncFuncName2() {
var def = q.defer();
//async function
setTimeout(function(){ //suppose
//do something
if(good) {
def.resolve();
} else {
def.reject();
}
}, 1000); //arbitrary timeout of 1 second
return def.promise;
}
USAGE:
q.all([someAsyncFuncName1('/api-1'), someAsyncFuncName2()])
.then(function() {
//final handler
});
On a similar line of thought one can use q.allSettled() if you want to wait for all promises to return.
Hope this helps.
---EOF OLD ANSWER---
First of all, if async functions used in PromiseA don't return promises, you need to promisify them. You can do that with Promise constructor, but it's much better to use libraries, such as bluebird with their promisify methods.
Let's imagine, that we have two functions getUserIdsAsync and getUserAsync. The first on returns a list of user ids, getUserAsync returns an user data by userId. And you need to get a list of users by their ids. The code of PromiseA could look so:
var promiseA = function() {
return getUserIdsAsync()
.then(userIds => {
let ops = users.map(uid => getUserAsync(uid));
return Promise.all(ops);
});
}
The following snippet shows a solution without using any external library like bluebird. It follows the code snippet in your question (which seems to be more complicate than needed).
You have to collect all api promisses in an array. Then you can call Promise.all() to get a Promise for the end of all api promisses. Then you can do some final stuff, like parsing the result of each promise and continue afterwards.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var apiEndpoint = function (name) {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve('API ' + name + ' job done'), 1000);
});
}
var promiseA = function() {
return new Promise( (resolve, reject) => {
const promisses = [];
for (var i=1; i < getRandomInt(3,6); i++) {
// 1. Establish objects needed from one API endpoint
promisses.push(apiEndpoint('This is number ' + i));
}
Promise.all(promisses).then( results => {
// do final stuff
for (const s of results) {
// 2. Call API endpoint for each object and parse
console.log(s);
}
// continue ...
// 3. Only then continue to next promise
resolve('now it is finished');
}).catch( err => reject(err) );
});
}
var finalPromise = function() {
return new Promise( (resolve, reject) => {
console.log('finalPromise');
resolve();
});
}
promiseA()
.then( () => finalPromise())
.catch(err => console.log(err) );
Please hold in mind that this solution is not easy to read. Using external libraries or reducing promisses can improve readability. Maybe you should take a look to the async/await pattern to get a much more better (readable) solution.
Here is a solution with async/await:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const apiEndpoint = function (name) {
return new Promise( (resolve, reject) => {
setTimeout(() => resolve('API ' + name + ' job done'), 1000);
});
}
async function promiseParallel () {
const promisses = [];
for (let i = 1; i < getRandomInt(3,6); i++) {
promisses.push(apiEndpoint('This is number ' + i));
}
for (const p of promisses) {
const x = await p;
console.log(x);
}
return ('everything is done');
}
promiseParallel().then( result => {
console.log(result);
}).catch( err => console.log(err) );
If you want call the promisses sequentially you can replace with:
async function promiseSequ () {
for (let i = 1; i < getRandomInt(3,6); i++) {
const x = await apiEndpoint('This is number ' + i);
console.log(x);
}
return ('everything is done');
}

error while using the Q library nodeJS

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.

How to resolve recursive asynchronous promises?

I'm playing around with promises and I'm having trouble with an asynchronous recursive promise.
The scenario is an athlete starts running the 100m, I need to periodically check to see if they have finished and once they have finished, print their time.
Edit to clarify :
In the real world the athlete is running on a server. startRunning involves making an ajax call to the server. checkIsFinished also involves making an ajax call to the server. The code below is an attempt to imitate that. The times and distances in the code are hardcoded in an attempt to keep things as simple as possible. Apologies for not being clearer.
End edit
I'd like to be able to write the following
startRunning()
.then(checkIsFinished)
.then(printTime)
.catch(handleError)
where
var intervalID;
var startRunning = function () {
var athlete = {
timeTaken: 0,
distanceTravelled: 0
};
var updateAthlete = function () {
athlete.distanceTravelled += 25;
athlete.timeTaken += 2.5;
console.log("updated athlete", athlete)
}
intervalID = setInterval(updateAthlete, 2500);
return new Promise(function (resolve, reject) {
setTimeout(resolve.bind(null, athlete), 2000);
})
};
var checkIsFinished = function (athlete) {
return new Promise(function (resolve, reject) {
if (athlete.distanceTravelled >= 100) {
clearInterval(intervalID);
console.log("finished");
resolve(athlete);
} else {
console.log("not finished yet, check again in a bit");
setTimeout(checkIsFinished.bind(null, athlete), 1000);
}
});
};
var printTime = function (athlete) {
console.log('printing time', athlete.timeTaken);
};
var handleError = function (e) { console.log(e); };
I can see that the promise that is created the first time checkIsFinished is never resolved. How can I ensure that that promise is resolved so that printTime is called?
Instead of
resolve(athlete);
I could do
Promise.resolve(athlete).then(printTime);
But I'd like to avoid that if possible, I'd really like to be able to write
startRunning()
.then(checkIsFinished)
.then(printTime)
.catch(handleError)
The bug is that you are passing a function that returns a promise to setTimeout. That promise is lost into the ether. A band-aid fix might be to recurse on the executor function:
var checkIsFinished = function (athlete) {
return new Promise(function executor(resolve) {
if (athlete.distanceTravelled >= 100) {
clearInterval(intervalID);
console.log("finished");
resolve(athlete);
} else {
console.log("not finished yet, check again in a bit");
setTimeout(executor.bind(null, resolve), 1000);
}
});
};
But meh. I think this is a great example of why one should avoid the promise-constructor anti-pattern (because mixing promise code and non-promise code inevitably leads to bugs like this).
Best practices I follow to avoid such bugs:
Only deal with async functions that return promises.
When one doesn't return a promise, wrap it with a promise constructor.
Wrap it as narrowly (with as little code) as possible.
Don't use the promise constructor for anything else.
After this, I find code easier to reason about and harder to bugger up, because everything follows the same pattern.
Applying this to your example got me here (I'm using es6 arrow functions for brevity. They work in Firefox and Chrome 45):
var console = { log: msg => div.innerHTML += msg + "<br>",
error: e => console.log(e +", "+ e.lineNumber) };
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var startRunning = () => {
var athlete = {
timeTaken: 0,
distanceTravelled: 0,
intervalID: setInterval(() => {
athlete.distanceTravelled += 25;
athlete.timeTaken += 2.5;
console.log("updated athlete ");
}, 2500)
};
return wait(2000).then(() => athlete);
};
var checkIsFinished = athlete => {
if (athlete.distanceTravelled < 100) {
console.log("not finished yet, check again in a bit");
return wait(1000).then(() => checkIsFinished(athlete));
}
clearInterval(athlete.intervalID);
console.log("finished");
return athlete;
};
startRunning()
.then(checkIsFinished)
.then(athlete => console.log('printing time: ' + athlete.timeTaken))
.catch(console.error);
<div id="div"></div>
Note that checkIsFinished returns either athlete or a promise. This is fine here because .then functions automatically promote return values from functions you pass in to promises. If you'll be calling checkIsFinished in other contexts, you might want to do the promotion yourself, using return Promise.resolve(athlete); instead of return athlete;.
Edit in response to comments from Amit:
For a non-recursive answer, replace the entire checkIsFinished function with this helper:
var waitUntil = (func, ms) => new Promise((resolve, reject) => {
var interval = setInterval(() => {
try { func() && resolve(clearInterval(interval)); } catch (e) { reject(e); }
}, ms);
});
and then do this:
var athlete;
startRunning()
.then(result => (athlete = result))
.then(() => waitUntil(() => athlete.distanceTravelled >= 100, 1000))
.then(() => {
console.log('finished. printing time: ' + athlete.timeTaken);
clearInterval(athlete.intervalID);
})
.catch(console.error);
var console = { log: msg => div.innerHTML += msg + "<br>",
error: e => console.log(e +", "+ e.lineNumber) };
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var waitUntil = (func, ms) => new Promise((resolve, reject) => {
var interval = setInterval(() => {
try { func() && resolve(clearInterval(interval)); } catch (e) { reject(e); }
}, ms);
});
var startRunning = () => {
var athlete = {
timeTaken: 0,
distanceTravelled: 0,
intervalID: setInterval(() => {
athlete.distanceTravelled += 25;
athlete.timeTaken += 2.5;
console.log("updated athlete ");
}, 2500)
};
return wait(2000).then(() => athlete);
};
var athlete;
startRunning()
.then(result => (athlete = result))
.then(() => waitUntil(() => athlete.distanceTravelled >= 100, 1000))
.then(() => {
console.log('finished. printing time: ' + athlete.timeTaken);
clearInterval(athlete.intervalID);
})
.catch(console.error);
<div id="div"></div>
Using setTimeout / setInterval is one of the scenrios that doesn't play well with promises, and causes you to use the frowned promise anti-pattern.
Having said that, if you reconstruct your function make it a "wait for completion" type of function (and name it accordingly as well), you'd be able to solve your problem. The waitForFinish function is only called once, and returns a single promise (albeit a new one, on top of the original promise created in startRunning). The handling of the recurrence through setTimeout is done in an internal polling function, where proper try/catch is used to ensure exceptions are propagated to the promise.
var intervalID;
var startRunning = function () {
var athlete = {
timeTaken: 0,
distanceTravelled: 0
};
var updateAthlete = function () {
athlete.distanceTravelled += 25;
athlete.timeTaken += 2.5;
console.log("updated athlete", athlete)
}
intervalID = setInterval(updateAthlete, 2500);
return new Promise(function (resolve, reject) {
setTimeout(resolve.bind(null, athlete), 2000);
})
};
var waitForFinish = function (athlete) {
return new Promise(function(resolve, reject) {
(function pollFinished() {
try{
if (athlete.distanceTravelled >= 100) {
clearInterval(intervalID);
console.log("finished");
resolve(athlete);
} else {
if(Date.now()%1000 < 250) { // This is here to show errors are cought
throw new Error('some error');
}
console.log("not finished yet, check again in a bit");
setTimeout(pollFinished, 1000);
}
}
catch(e) { // When an error is cought, the promise is properly rejected
// (Athlete will keep running though)
reject(e);
}
})();
});
};
var printTime = function (athlete) {
console.log('printing time', athlete.timeTaken);
};
var handleError = function (e) { console.log('Handling error:', e); };
startRunning()
.then(waitForFinish)
.then(printTime)
.catch(handleError);
While all this code is functioning properly, a polling solution is never advised in an asynchronous environment and should be avoided if possible. In your case, since this sample mocks communication with a server, I'd consider using web sockets if possible.
Since your use of promises is pretty off the mark, it's a little hard to tell exactly what you're trying to do or what implementation would best fit, but here's a recommendation.
Promises are a one-shot state machine. As such, you return a promise and exactly one time in the future, the promise can be either rejected with a reason or resolved with a value. Given that design of promises, I think what makes sense would be something that can be used like this:
startRunning(100).then(printTime, handleError);
You could implement that with code like this:
function startRunning(limit) {
return new Promise(function (resolve, reject) {
var timeStart = Date.now();
var athlete = {
timeTaken: 0,
distanceTravelled: 0
};
function updateAthlete() {
athlete.distanceTravelled += 25;
console.log("updated athlete", athlete)
if (athlete.distanceTravelled >= limit) {
clearInterval(intervalID);
athlete.timeTaken = Date.now() - timeStart;
resolve(athlete);
}
}
var intervalID = setInterval(updateAthlete, 2500);
});
}
function printTime(athlete) {
console.log('printing time', athlete.timeTaken);
}
function handleError(e) {
console.log(e);
}
startRunning(100).then(printTime, handleError);
Working demo: http://jsfiddle.net/jfriend00/fbmbrc8s/
FYI, my design preference would probably be to have a public athlete object and then methods on that object to start running, stop running, etc...
Here are some of the fundamental things you got wrong in a use of promises:
They are one-shot objects. They are resolved or rejected only once.
The structure startRunning().then(checkIsFinished) just doesn't make logical sense. For the first part of this to work, startRunning() has to return a promise, and it has to resolve ore reject that promise when something useful happens. Your are just resolving it after two seconds which doesn't really seem to accomplish anything useful.
The words of your description make it sound like you want `checkIsFinished() to keep going an not resolve its promise until the athlete is finished. It is possible to do that by continually chaining promises, but that seems a very complicated way to do things and certainly not necessary here. Further, that isn't at all what your code attempts to do. Your code just returns a new promise that is never resolved unless the athelete has already passed the desired distance. If not, it returns a promise that is never resolved or rejected. This is a fundamental violation of promise concepts. A function that returns a promise is responsible for eventually resolving or rejecting it unless the calling code expects to just abandon the promise in which case it's probably the wrong design tool.
Here's another approach that creates a public Athlete() object that has some methods and allows multiple people to be watching the progress:
var EventEmitter = require('events');
function Athlete() {
// private instance variables
var runInterval, startTime;
var watcher = new EventEmitter();
// public instance variables
this.timeTaken = 0;
this.distanceTravelled = 0;
this.startRunning = function() {
startTime = Date.now();
var self = this;
if (runInterval) {clearInterval(runInterval);}
runInterval = setInterval(function() {
self.distanceTravelled += 25;
self.timeTaken = Date.now() - startTime;
console.log("distance = ", self.distanceTravelled);
// notify watchers
watcher.emit("distanceUpdate");
},2500);
}
this.notify = function(limit) {
var self = this;
return new Promise(function(resolve, reject) {
function update() {
if (self.distanceTravelled >= limit) {
watcher.removeListener("distanceUpdate", update);
resolve(self);
// if no more watchers, then stop the running timer
if (watcher.listeners("distanceUpdate").length === 0) {
clearInterval(runInterval);
}
}
}
watcher.on("distanceUpdate", update);
});
}
}
var a = new Athlete();
a.startRunning();
a.notify(100).then(function() {
console.log("done");
});

NodeJS Promise behaviour query

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);
});

Categories