Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there idiomatic ways to express "if (c1 || c2)" in JavaScript when one of the conditions is a promise and the other is not?

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.

like image 826
hippietrail Avatar asked Jun 19 '16 13:06

hippietrail


3 Answers

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 => { … });
like image 116
Bergi Avatar answered Nov 20 '22 08:11

Bergi


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...

like image 38
hippietrail Avatar answered Nov 20 '22 07:11

hippietrail


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.

like image 1
Benjamin Gruenbaum Avatar answered Nov 20 '22 08:11

Benjamin Gruenbaum