Reputation: 5542
I'm kind of beginning to learn javascript and have a question about combining synchronous and asynchronous calls to functions. This will be just a theoric problem, but I hope it transmits the idea.
Let's suppose we have javascript program that decides how many bananas and oranges I need to buy.
console.log('buy %d bananas and %d oranges', bananas, oranges)
Now, I can decide how many bananas I can buy, but I need to ask my wife how many oranges she wants, so I text her. (I can write an async function to represent this).
This would be my immediate approach:
var bananas = 10;
var oranges = 0;
textWife('askAboutOranges',function(number){ oranges = number; }
console.log('buy %d bananas and %d oranges', bananas, oranges)
But to me this doesn't make sense, because I have to wait for my wife to reply, so I probably won't have the number of oranges in time.
So I can change my program to:
var bananas = 10;
var oranges = 0;
textWife('askAboutOranges',function(number){
oranges = number;
console.log('buy %d bananas and %d oranges', bananas, oranges);
}
But I don't like this because now I have the logic of deciding what to buy including the bananas, inside the response from my wife. What if I decide I don't want oranges, do I have to do something like this:
var bananas = 10;
var oranges = 0;
if (wantOranges)
{
textWife('askAboutOranges',function(number){
oranges = number;
console.log('buy %d bananas and %d oranges', bananas, oranges);
}
}
else
console.log('buy %d bananas and %d oranges', bananas, oranges);
So my question is, can anyone explain me what's the best/right way to do something like this?
Upvotes: 6
Views: 2226
Reputation: 31929
// setup
var bananas = 4
var wantOranges = true
function textWife() { return new Promise((resolve, reject) => setTimeout(() => resolve(8), 1000)) }
// execution
Promise.all([
bananas,
wantOranges ? textWife() : 0 // ternary is necessary here because boolean logs as `NaN`
]).then(([bananas, oranges]) => console.log('buy %d bananas and %d oranges', bananas, oranges))
Note: I am using ..
Promises
Destructuring assignment where var [bananas, oranges] = [4, 8]
is same as var bananas = 4, oranges = 8
.
Arrow functions () => {}
Upvotes: 2
Reputation: 214949
There's no right/best way - it always depends. Modern Javascript libraries use concepts like Deferreds and Promises to synchronize between multiple async calls, sync calls and immediate data. Using jquery, your code might be written as:
$.when({bananas:10, oranges:1}, textWife('askAboutOranges')).done(function(myList, herList) {
buyBananas(myList.bananas);
buyOranges(myList.oranges + herList.oranges);
});
See http://api.jquery.com/category/deferred-object/ for more information.
And this is how to create Deferred objects in your code, in an elegant way:
function decideHowManyOranges() {
if (!wantOranges)
return 0;
return $.Deferred(function(d) {
textWife('askAboutOranges', function(number) {
d.resolve(number);
})
})
}
numOfBananas = 10
$.when(numOfBananas, decideHowManyOranges()).done(function(bananas, oranges) {
console.log('buy %d bananas and %d oranges', bananas, oranges);
});
Upvotes: 3
Reputation: 27012
jQuery Deferred is a great tool to have in your belt. I might do something like this to separate concerns:
function decideHowManyBananas() {
return 10;
}
function decideHowManyOranges() {
var deferred = $.Deferred();
if (wantOranges) {
textWife('askAboutOranges', function(number) {
deferred.resolve(number);
});
} else {
deferred.resolve(0);
}
return deferred.promise();
}
$.when(decideHowManyBananas(), decideHowManyOranges()).done(function(bananas, oranges) {
console.log('buy %d bananas and %d oranges', bananas, oranges);
});
Upvotes: 7
Reputation: 17357
In your first example, you state that the answer to the question "how many bananas and oranges should I buy?" is dependent on both information you possess, i.e. the number of needed bananas, as well as information you do not possess, the number of needed oranges, so you cannot answer it without first acquiring the missing information.
You then refactor your code to defer answering your question till you possess the missing information which is the typical approach to solving this kind of dependency.
You then state that this doesn't satisfy you because you may not wish to purchase any oranges at all. This is a new requirement and so invalidates your original question, changing it to "how many bananas should I buy?"
By removing the requirement for the missing information, you change the question and so need to special case your problem into two questions.
The final code example you provide does this by first deciding if you need to purchase both bananas and oranges or just oranges, which is indeed the most straightforward way of solving your problem.
Upvotes: 0
Reputation: 15278
Prepare a function and use it in both cases to remove code duplication:
var bananas = 10;
var oranges = 0;
function buyStuff() {
console.log('buy %d bananas and %d oranges', bananas, oranges);
}
if (wantOranges)
textWife('askAboutOranges',function(number){
oranges = number;
buyStuff();
})
else
buyStuff();
Upvotes: 0