When I only perform the next steps in my algorithm if various conditions are met I express it this way:
if (sc1 || sc2) {
do();
various();
things();
}
When I only perform the next steps based on the fulfillment of a promise I can express it like this:
asyncCheck().then(ac1 => {
if (ac1) {
do();
various();
things();
}
}
How can I express in idiomatic JavaScript when condition sc1
is a just a regular old synchronous expression but condition ac2
comes asynchronously via a promise?
Assume native ES6 promises and nontrivial code that gets executed if the conditions are met.
For instance, this "obvious" way seems ugly:
if (sc1) {
do();
various();
things();
} else {
asyncCheck().then(ac2 => {
if (ac2) {
do();
various();
things();
}
}
}
I could put the repeated code in a function that gets called either way, which is less ugly, but I feel like I could be missing something more idiomatic that other JavaScript programmers might be using.
I should add this observation too: Since in my case, there is a logical or, it should short circuit so it shouldn't bother with the slow deferred check if the simple check is already false
.
Pretty simple actually:
Promise.resolve(sc1 || asyncCheck()).then(cond => {
if (cond) {
do();
various();
things();
}
});
Admittedly the truthiness of sc1
is possibly evaluated twice, but otherwise it does short-circuit. You can also use
(sc1 ? Promise.resolve(true) : asyncCheck()).then(cond => { … });
Thinking more and with everybody's help here, this is my current evolution:
function ifConditionsMet() {
if (sc1) {
return Promise.resolve(true);
} else {
return asyncCheck();
}
}
ifConditionsMet().then(() => {
do();
various();
things();
});
I this this should work fine with any exception handling too...
Well no, promises just abstract values. While it is possible to express control flow structures with promises I don't think it's a good idea.
function or(v1, v2, ifPath, elsePath) {
v1 = Promise.resolve(v1);
v2 = Promise.resolve(v2);
return Promise.all([v1, v2])
.then(([v1, v2]) => (v1 || v2) ? ifPath() : elsePath());
}
Which would let you do:
or(ac1, ac2, () => {
do();
various();
things();
}, () => {});
But honestly that's a pretty bad idea in general. It's leaky (no short circuit) and doesn't compose with other abstractions. You can do a much more basic:
Promise.all([ac1, ac2]).then(([r1, r2]) => {
if(r1 || r2) {
do();
various();
things();
}
});
Which is easier and easy to compose with. In the next version of JavaScript (ES8, ES2017) you'll very likely have async
/await
which would let you do the following directly:
// the function containing this needs to be marked as `async`
if (sc1 || await sc2) {
do();
various();
things();
}
Which will make things easier. This is still a little leaky but much much less so.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With