Hey guys, just figured out a trick with promises that makes the code look cleaner, is more readable, and just plain sweet!
Problem:
function recipeForSuccess(){
return barAsync()
.then(function(barData){
return fooAsync(barData);
})
.then(function(fooData){
// getFooBar requires fooData and myBar
// but I only have access to fooData within this functions scope
return getFooBar(fooData, myBar);
})
.catch(function (err) {
console.log(err);
});
}
Solutions:
The "Old" Way:
Whenever you needed access to data down the promise chain you tied it to a variable scoped outside of your promise chain.
Example:
function recipeForSuccess(){
var myBar; // will be defined when the barAsync promise is resolved
return barAsync()
.then(function(barData){
myBar = barData; // defining myBar for use further down the promise chain.
return fooAsync(barData);
})
.then(function(fooData){
// now I have access to the data returned from barAsync by using myBar.
return getFooBar(fooData, myBar);
})
.catch(function (err) {
console.log(err);
});
}
Downsides: This clutters the code up and when you read it from top to bottom you have to bounce around to figure out where myFoo is coming from / being defined.
The "New" Way:
Instead of declaring myFoo at the beginning and then assigning data to it half way through the Promise chain, why don't we just pass it to the next Promise in the chain? This improves the code by allowing the code to flow from top to bottom only and removes unnecessary variable declarations that clutter up the code.
function recipeForSuccess(){
return barAsync()
.then(function(barData){
return Q.all([
fooAsync(barData),
barData
]);
})
.then(function(data){
// data[0] will contain whatever is returned by barAsync(fooData);
// data[1] will contain fooData
return getFooBar(data[0], data[1]);
})
.catch(function (err) {
console.log(err);
});
}
You can also write it like this:
function recipeForSuccess(){
return barAsync()
.then(function(barData){
return Q.all([
fooAsync(barData),
barData
]);
})
.spread(function(fooData, barData){
return getFooBar(fooData, barData);
})
.catch(function (err) {
console.log(err);
});
}
Another way to handle this is to just return the variable from the promise and pass it down the chain. For example:
ReplyDeletefunction makeCake() {
var oven = new Oven();
var bowl = new Bowl('3qt');
var pan = new Pan('13x9');
oven.preheat(350)
.then(getIngredients)
.then(mixIngredients)
.then(bake)
.then(eat)
.catch(giveToDog);
function getIngredients() {
return ['flour', 'sugar', 'eggs', 'milk'];
}
function mixIngredients(ingredients) {
ingredients.forEach(function (i) {
bowl.add(ingredient);
});
return bowl.beatFor('30s'); // This is a promise, which returns the bowl
}
function bake(bowl) {
bowl.pourInto(pan);
return oven.bake(pan, '32m'); // This is a promise, which returns the baked cake
}
function eat(cake) {
return 'yum';
}
function giveToDog(ruined) {
return 'gulp';
}
}