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 :)
Related
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.
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.
Because I'm using a prototype that has functions calling other functions in the same prototype I have to refer to that method using this
The Problem this Created:
But because of that, I have to preserve a context to use this that has me forming very ugly .bind(this) walls.
Here is a simplified example I made for laughs.
Killmyself.prototype.fireLeMissles = function () {
return new Promise(function(resolve,reject) {
this.anotherFunction(param).then(function(result) {
someList.forEach(function(item) {
this.fireLeMissles().then(function(anotherResult){
promiseList.push(anotherResult)
})
},this);
Promise.all(promiseList).then(function(promiseItem){
childPlacesIds.forEach(function(childPlaceId) {
//Do Other Stuff
},this);
});
resolve(result);
}.bind(this).catch(function(err){
console.log("Yea, life sucks sometimes.")
}));
}.bind(this));
}
Killmyself.prototype.another = function(){
//Other stuff
}
You can see because of calls to functions in the same prototype such as this.anotherFunction(... and this.fireLeMissles(... I had to do deep preservation of context,which now (in my much larger version of this) is making this code hard to work with.
Question:
Is this a "man up and get use to the harder aspects of JavaScript" thing - or do you seasoned developers see simple ways that deep binding like this could have been avoided?
If you are using ES6, you can benefit from arrow functions, which preserve the context.
var counter = function () {
this.count = 0;
setInterval( () => { // arrow function
console.log(this.count++); // context is preserved
}, 1000)
}
var counter = new counter();
So, your code would become something like:
Killmyself.prototype.fireLeMissles = function() {
return new Promise((resolve, reject) => {
this.anotherFunction(param).then(result => {
someList.forEach(item => {
this.fireLeMissles().then(anotherResult => {
promiseList.push(anotherResult)
});
});
Promise.all(promiseList).then(promiseItem => {
childPlacesIds.forEach(childPlaceId => {
//Do Other Stuff
});
});
resolve(result);
}).catch(err => {
console.log("Yea, life sucks sometimes.")
});
});
}
For ES5, you can just either use .bind just like the way you did or you can assign this to something else in the function with the desired context, then use that variable inside the inner functions.
Killmyself.prototype.fireLeMissles = function() {
var self = this; /// use `self` instead of `this` from now on.
return new Promise(function(resolve, reject) {
self.anotherFunction(param).then(function(result) {
someList.forEach(function(item) {
self.fireLeMissles().then(function(anotherResult) {
promiseList.push(anotherResult)
})
});
Promise.all(promiseList).then(function(promiseItem) {
childPlacesIds.forEach(function(childPlaceId) {
//Do Other Stuff
});
});
resolve(result);
}).catch(function(err) {
console.log("Yea, life sucks sometimes.")
});
});
}
for starters I do not understand you you need a new Promise.. here, like #loganfsmyth said, I would simply use arrow functions and reduce the complexity:
Killmyself.prototype.fireLeMissles = function (param) {
return this.anotherFunction(param)
.then(someList => {
var promiseList = someList.map( item => this.fireLeMissles(item));
return Promise.all(promiseList);
}).then(childPlacesIds => {
childPlacesIds.forEach(childPlacesId = {
// .... do something;
});
// return something.
}).catch(err => console.log("Yea, life sucks sometimes."));
}
P. S: I am not sure where this param, someList, childPlacesIds is coming from, and assumed that you are initializing that promiseList as empty array.
Mido's answer is good, I just wanted to provide an alternative take on it which I think would be useful to know - using promises for the proxies they are:
Killmyself.prototype.fireLeMissles = function () {
let fn = this.anotherFunction(param);
let others = fn.then(_ => someList.map(this.fireLeMissles, this));
let othersP = Promise.all(others);
othersP.then(/* do OtherStuff */);
return othersP; // or whatever its then returned
}
Of course, this gets even easier with a library like bluebird.
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)
I'm using D.js as promise library for our javascript application.
Following is my sample code:
function getData(deferred) {
var data_one;
// getInfo is returning a promise for async task
getInfo()
.then(function (resp_one) {
data_one = resp_one;
// getInfo2 is also returning another promise
return getInfo2();
})
.then(function (resp_two) {
deferred.resolve('prefix' + data_one + resp_two);
});
};
function sample () {
var d = D(),
data = localStorage.getItem('key');
if (data) {
d.resolve(data);
} else {
getData(d);
}
return d.promise;
}
sample().then(function (data) {
//do something with data.
});
I im invoking sample function. Is the implementation inside the sample function and sub functions following the coding standard for promises?
Im new to promises, Is it good to passing around deferred object to other function to resolve/reject ??
Is there any better way to implement the above functionality??
Thanks in advance..
Looks like you can improve the code if you use promises in more natural way.
First of all if getData returns a Promise then you don't have to pass deferred around, this is considered anti-pattern. You just simply return getInfo().
Another thing, in the sample function if your data might be already available it's convenient to use D.promisify method to return resolved promise with non-promise value:
function getData() {
var data_one;
return getInfo().then(function (resp_one) {
data_one = resp_one;
return getInfo2();
})
.then(function (resp_two) {
return 'prefix' + data_one + resp_two;
});
};
function sample() {
var data = localStorage.getItem('key');
return data ? D.promisify(data) : getData();
}
sample().then(function (data) {
//do something with data.
});