24

I have a conditional statement in which I need to perform one of two operations, then continue after whichever operation has resolved. So my code currently looks as follows:

if (shoud_do_thing_a) { //should_do_thing_a is just a variable that determines which function to call. it is not a promise
  do_thing_a()
} else {
  do_thing_b()
}

// more code

The issue is that both do_thing_a and do_thing_b return promises, and I can't move on until whichever gets executed has resolved. The best way I've come up with to solve this is like this:

var more_code = function () { 
  // more code
}

if (shoud_do_thing_a) {
  do_thing_a().then(more_code)
} else {
  do_thing_b().then(more_code)
}

I don't like this structure. It's difficult to follow because you need to jump around to find where more_code is defined (imagine I have this type of control flow in several locations), rather than simply being able to continue reading.

Is there a better way to deal with this type of thing in javascript?

ewok
  • 20,148
  • 51
  • 149
  • 254

10 Answers10

22

If you can use async/await

async function someFunc() {
    var more_code = function () { 
        // more code
    }

    if (shoud_do_thing_a) {
        await do_thing_a()
    } else {
        await do_thing_b()
    }

    more_code()
}

Or if you can't, use then():

var more_code = function () { 
    // more code
}

var do_thing;
if (shoud_do_thing_a) {
  do_thing = do_thing_a()
} else {
  do_thing = do_thing_b()
}

do_thing.then(more_code)
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
CRice
  • 29,968
  • 4
  • 57
  • 70
  • `async/await` requires `async` to be used when defining function – guest271314 Nov 02 '17 at 20:15
  • 1
    @guest271314 Indeed, but since OP didn't include a function definition in his snippet, I left it out. I'll edit the post to include it. – CRice Nov 02 '17 at 20:17
  • `async/await` isn't working. I get a `SyntaxError: Unexpected identifier` when I try to define `async func() { ... }` – ewok Nov 02 '17 at 20:29
  • @ewok async/await is only available in newer versions of javascript. If this code is running in the browser, you'll have to use promises alone, or use a transpiler (babel/typescript/etc.). If you're using node, async/await will work in any 7+ version I believe. – CRice Nov 02 '17 at 20:31
8

If you're stuck with raw Promises and can't use async/await (You usually should have no trouble, what with babel/typescript etc), the following is a bit more elegant than storing the promise in a variable:

function something() {
  return Promise.resolve()
    .then(() => {
      if (should_do_thing_a) {
        return do_thing_a();
      }
      else if (should_do_thing_b) {
        return do_thing_b();
      }
    })
    .then(some_more_code);
}

Note that when you start working with Promises, your functions should always return a Promise that other functions can work with. Leaving an asynchronous action without any way to handle it means bad things, especially when it comes to error handling.

In a more general sense, it means that when you use Promises, more of your code is "uplifted" into being executed and returned as Promises.

Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
  • your point is true but your code looks ugly. I cleaned it up for you. (too bad you cant have multiline in a comment, now my formatting is gone) `function doTheThing () { if (shouldDoA) return doThingA() else return doThingB() } doTheThing().then(moreCode)` – ThaJay May 28 '18 at 13:53
  • I posted it in another answer so the point comes across a bit better. – ThaJay May 28 '18 at 14:01
4

How I want to improve on other answers:

  • keep it clean and simple
  • no unneeded variables
  • return promise asap
  • in js we use camelCase
  • put it in a function and name that function to keep it readable
  • let then execute moreCode so it's called after the thing is done.

function doTheThing () {
  if (shouldDoA) return doThingA()
  else return doThingB()
}

doTheThing().then(moreCode)
ThaJay
  • 1,758
  • 1
  • 19
  • 30
3

Simple working example:

The scope it's defined in must be async.

const createUser = async (data) => {
   if (await isUsernameTaken(username)) { return 'username-taken' }
}

The isUsernameTaken func:

const isUsernameTaken = async (username) => {
    const request = await API.SomeRequest
    return !request.isEmpty
}
Oliver Dixon
  • 7,012
  • 5
  • 61
  • 95
1

Save the promise and add the then after the if statement:

var promise;

if (shoud_do_thing_a) {
  promise = do_thing_a();
} else {
  promise = do_thing_b();
}

promise.then(more_code);
Nachshon Schwartz
  • 15,289
  • 20
  • 59
  • 98
1
var promise = shoud_do_thing_a? do_thing_a: do_thing_b

promise().then(function () {
// more code    
})
Peter
  • 1,742
  • 15
  • 26
1

Similar to other answers here, but you can self execute the async and clean up the condition a bit.

(async () => {
  const should_do_thing_a = true

  const do_thing_a = function() {
    return new Promise(function(resolve, reject) {
      resolve('a')
    })
  }

  const do_thing_b = function() {
    return new Promise(function(resolve, reject) {
      resolve('b')
    })
  }

  const result = (should_do_thing_a) ? await do_thing_a() : await do_thing_b()

  console.log(result)

})()
dzm
  • 22,844
  • 47
  • 146
  • 226
1

The way I would do it would be to put the if check into another function that returns a promise. The promise gets resolved with the resolve of the other function calls in the if-else statement.

Example:

function performCheck(condition) {
    var defer = $q.defer();
    if (condition) {
        doThingA().then(function(response) {
            defer.resolve(response);
        });
    } else {
        doThingB().then(function(response) {
            defer.resolve(response)
        });
    }
    return defer.promise;
}
performCheck(condition).then(function(response) {
    //Do more code.
});

In my opinion, I would prefer this method because this function can now be used in multiple places where you have a check on the condition, reducing code duplication, and it is easier to follow.

You could reduce this down further with

function performCheck(condition) {
    var defer = $q.defer();
    var doThisThing = condition ? doThingA : doThingB;
    doThisThing().then(function (response) {
        defer.resolve(response);
    });
    return defer.promise;
}
performCheck(condition).then(function(response) {
    //Do more code.
});
CrazyJane
  • 63
  • 10
0

You can use async/await

async function fn() {
  let p, result;
  if (shoud_do_thing_a) {
    p = await do_thing_a()
  } else {
    p = await do_thing_b()
  }
  if (p) {
    result = more_code();
  }
  return result
}
guest271314
  • 1
  • 15
  • 104
  • 177
  • Hmm? Why are you awaiting the conditional and not the functions `do_thing_a` and `do_thing_b`? – CRice Nov 02 '17 at 20:15
  • @CRice From original reading of code at Question `shoud_do_thing_a` is the variable which represents a `Promise`, else there is no issue at code at Question – guest271314 Nov 02 '17 at 20:16
-1
more_code = miFunc() => return new Promise((resolve, reject) => ... });

Solution 1

const waitFor = should_do_thing_a ? do_thing_a() : do_thing_b();
waitFor.then(...).catch(...)

Solution 2

let waitFor = Promise.resolve();

if (do_thing_a) {
    waitFor = do_thing_a();
} else {
    waitFor = do_thing_b();
}

waitFor.then(...).catch(...);
Jose Mato
  • 2,709
  • 1
  • 17
  • 18