Having if-else condition inside promises chains - javascript

I have a promises chain and inside some points I have if-else condition like the following:
.then(function() {
if(isTrue) {
// do something returning a promise
} else {
// do nothing - just return
return;
}
})
.then(function() {
...
})
Honestly I don't like this pattern. I feel it wrong. I mean using just a plain return without anything. Do you have some idea to make look different this code?

That else { return; } part can simply be completely omitted without changing the code's meaning:
.then(function() {
if (isTrue) {
// do something returning a promise
}
})
Functions do return undefined anyway by default.

I guess you have tested the code. And recognized that this is not working like you expected. Let me explain you:
function getPromise() {
callSomeFunctionWhichReturnsPromise().then(function(result) {
return result; // You hope, that this will be logged on the console? nope, you have to do it here instead.
console.log('logged in the promise', result); // This will work
});
}
var result = getPromise();
console.log(result); // undefined!!!
you could instead do this:
function getPromise() {
return callSomeFunctionWhichReturnsPromise();
}
var result = getPromise();
result.then(console.log); // will call console.log(arguments)

Related

JavaScript: Recursive function with Promise, resolve returns "undefined"

I have a recursive function which is returning promise, there is some conditions which on fulfillment the resolve will fire. But the resolve always returns undefined.
Here is the code:
var children = [];
var temp = [];
function getRelationsOfRep(repId) {
var index;
return new Promise(function (resolve, reject) {
return RepRelation.findOne({_id: repId}).then(function (repData) {
if (<condition>) {
temp = temp.concat(repData.children);
temp.forEach(function (childId) {
return getRelationsOfRep(childId);
});
} else {
// else
}
if (<another condition>) {
return resolve(children);
}
}).catch(function (error) {
return reject(error);
})
})
}
function updateRepData(id){
children = [];
temp = [];
getRelationsOfRep(id).then(function (branchesData) {
console.log('branchesData:: ', branchesData); // it prints undefined
})
}
I'm trying to get all children of a Representative and the children of its children (if any) and return them all.
What is it that I'm doing wrong?
Any help would be much appreciated.
Thanks in advance.
Use Promise.all() to capture all the promises in Loop.
Instead of:
temp.forEach(function (childId) {
return getRelationsOfRep(childId);
});
Try:
var promisesArray = [];
temp.forEach(function (childId) {
promisesArray.push(getRelationsOfRep(childId));
});
return Promise.all(promisesArray);
Using something like this, you will be able to get nested response for the recursive calls.
P.S. Not tested, modify according to the logic you want.
You are making things slightly complicated. I think what you want to achieve is to use promise to return a result from a recursive function.
Here's a cleaner version
A cleaner solution -
getRelationsOfRep(repId) {
return new Promise( function(resolve,reject) {
recursiveFunctionCall(repId);
function recursiveFunctionCall(repId) {
//your recursive function logic
recursiveFunctionCall(childId);
if(...) //edge case condition
resolve('your-answer');
else
reject('your-error')
}
});
This way, you'll just be using one promise and returning when your recursive function resolves.

How to break a promise chain?

I'm aware that there are similar questions, but I haven't seen any that address this chaining pattern.
I have the following:
var runTests = function (chain, resolutionTest) {
return chain.then(function (result) {
if (result)
return result; // Early return if the previous tests were successful. This is where I want to prevent other attempts.
const attempt = tryOpenStream(resolutionTest).then(streamToDom);
return attempt;
});
}
// from someplace else
numTests = resolutionTests.length;
return resolutionTests.reduce(runTests, Promise.resolve()); // start reduce with an empty promise
The problem that I'm encountering is that I'm calling tryOpenStream many times even after I've captured a result.
Options I'm considering:
Raise some global flag that just prevents further execution from within the chain. Yuck, because the chain still proceeds, it's just emptied.
throw new Error(result) instead of return result. This would break the chain (I think...) but it's misusing Error and would be easily misunderstood by another developer.
How can I break this chain at return result;?
UPDATE 1
I'm trying the following:
var makeTest = function (runMoreTests, resolutionTest) {
return function runTest() {
return tryOpenStream(resolutionTest).then(streamToDom).then(function (result) {
if (result)
return result;
else
return runMoreTests();
});
};
}
return resolutionTestBuilder.buildTests().then(function (resolutionTests) {
numTests = resolutionTests.length;
return resolutionTests.reduceRight(makeTest, function () { Promise.reject("No resolutions succeeded.") })();
});
However no calls to runTest are invoked. This is a bit of new syntax for me so I'll research some and update with any findings.
UPDATE 2
I was missing the () to invoke the reduceRight. Though now I'm seeing that reject called even with success... though when I step through, that rejection isn't invoked. It's as if by the time I get a result back, all links in the chain have been invoked.
Both flags and exceptions can be used, but as you noticed they're not the proper tool.
Instead, use recursion, like in #IsiahMeadows' answer, or a right fold:
var makeTest = function (runMoreTests, resolutionTest) {
return function runTest(result) {
if (result)
return result;
return tryOpenStream(resolutionTest).then(streamToDom).then(runMoreTests);
};
}
return Promise.resolve(resolutionTests.reduceRight(makeTest, x => x)(undefined));
or better written as
var makeTest = function (runMoreTests, resolutionTest) {
return function runTest() {
return tryOpenStream(resolutionTest).then(streamToDom).then(result => {
if (result)
return result;
else
return runMoreTests();
});
};
}
return resolutionTests.reduceRight(makeTest, () => Promise.reject("nothing succeeded"))();
Try using this:
function runTests(resolutionTest) {
return tryOpenStream(resolutionTest).then(streamToDom)
}
// from someplace else
function loop(tests, i) {
if (i === tests.length) return undefined
return runTests(tests[i]).then(function (result) {
if (result) return result
return loop(tests, i + 1)
})
}
return loop(resolutionTests, 0)
Although I do wonder why you can't use an exception to denote your tryOpenStream failed. That would actually simplify your code some.

What is the best way to write readable code using promises?

I have some node.js promise code that looks something like this:
function myFunc(data) {
Q(data).then(function(data) {
return getPromise1(globalVar).then(function(res1a) {
return getPromise2(JSON.parse(param2)).then(function(res2a) {
return doStuff();
}).then(function(res2b) {
return getPromise3(data).then(function(res3a) {
return getPromise4.then(function(res4a) {
// more stuff
})
})
})
})
})
})
As you can see, this code is not very readable. Is there a better way to do this?
If you don’t need all of the results at once, just stop treating promises like callbacks:
function myFunc(data) {
Q(data).then(function(data) {
return getPromise1(globalVar);
}).then(function(res1a) {
return getPromise2(JSON.parse(param2));
}).then(function(res2a) {
return doStuff();
}).then(function(res2b) {
return getPromise3(data);
}).then(function(res3a) {
return getPromise4;
}).then(function(res4a) {
// more stuff
})
})
If you do, then you can try coroutines given generator function support (Q probably has something for that, but here’s a Bluebird way):
var myFunc = bluebird.coroutine(function* myFunc(data) {
var res1a = yield getPromise1(globalVar);
var res2a = yield getPromise2(JSON.parse(param2));
var res2b = yield doStuff();
var res3a = yield getPromise3(data);
var res4a = yield getPromise4;
// more stuff
})
or synchronous inspection:
function myFunc(data) {
var res1a = getPromise1(globalVar);
var res2a = res1a.then(function() {
yield getPromise2(JSON.parse(param2));
});
var res2b = res2a.then(function() {
// feel free to use res1a.value() here;
// you know that it has to have been resolved
doStuff();
});
// …
return res4a;
}
One of the goals of promises besides separating data parameters from control flow parameters was actually to solve this issue of huge triangular blocks of code.
function myFunc(data) {
Q(data).then(function(data) {
return getPromise1(globalVar);
}).then(function(res1a) {
return getPromise2(JSON.parse(param2));
}).then(function(res2a) {
return doStuff();
}).then(function(res2b) {
return getPromise3(data);
}).then(function(res3a) {
return getPromise4;
}).then(function(res4a) {
// more stuff
})
}
Now, the only reason you would ever need to nest a promise is if you need to use data returned from a promise in a function not immediately following it. See below:
doAsyncA().then(function(x) {
doAsyncB().then(function(y) {
doSyncUsingBothReturns(x, y);
})
})
In your example it would help to use lambda expressions:
Q(data)
.then(data => getPromise1(globalVar)
.then(re1a => getPromise2(JSON.parse(param2)
and so on.
Without nesting and in this style, it looks much less like callback hell :)

Jquery .then not working

I have an accordion, and want to trigger something when it has finished transitioning from one state to another. The following code is throwing up an error Uncaught TypeError, I am just trying to console.log when it has finished for now:
$(document).ready(function () {
$('.accordion-tabs').each(function() {
$(this).children('li').first().children('a').addClass('is-active').next().addClass('is-open').show();
});
$('.accordion-tabs').on('click', 'li > a.tab-link', function(event) {
if (!$(this).hasClass('is-active')) {
event.preventDefault();
var accordionTabs = $(this).closest('.accordion-tabs');
accordionTabs.find('.is-open').removeClass('is-open').hide();
$(this).next().toggleClass('is-open').toggle();
accordionTabs.find('.is-active').removeClass('is-active');
$(this).addClass('is-active').then(
function() {
console.log( "Accordion Finished" );
});
} else {
event.preventDefault();
}
});
});
Where am I going wrong? This is the first time I have used .then!
yes it's not working, this is not the way of using it you need to learn promises first
It's used to replace (or provide an alternate way) the old callback mechanism with a cleaner way to handle asynchronous requests, instead of passing your callbacks as parameters, you can chain your function with .then, given function will be executed once the promise gets resolved.
Anyhow, this is just a basic explanation, you should really get into the books of promises for more info.
a simple example of :
var promise = new Promise(function(resolve, reject) {
if (true /* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
promise.then(function (x) { // Suppose promise returns "abc"
console.log(x);
return 123;
}).then(function (x){
console.log(x);
}).then(function (x){
console.log(x)
})

Why prototype function returns nothing in javascript?

My javascript prototype function like below;
ServerDataEngine.prototype.ExecuteCommand = function(command)
{
try
{
var result;
$.get(command, function (data) {
result = jQuery.parseJSON(atob(data));
console.log(result);
});
return result;
}
catch (Exception)
{
throw (new Exception("Can not connect to server"));
}
}
And I call this function like this;
ServerDataEngine.prototype.ExecuteQuery = function (query)
{
console.log(this.ExecuteCommand(query));
}
In ExecuteCommand, everything is ok, but in ExecuteQuery, console.log(this.ExecuteCommand(command)) line produces undefined.
What is the problem?
You are treating an Asynchronous Request as a synchronous one. You are returning the value before the Ajax call ever returns a value. It is not going to be able to work that way.

Categories

Resources