Promises are Not for Control Flow

JavaScript Promises are No for Control Flow

Common advice in languages with exceptions is that exceptions are not for control flow. A rejected promise is not any different than a thrown exception: in both cases something when exceptionally wrong and must be dealt with.

This week I was fighting with promises while building a slack app because I was attempting to use them for control flow.

The gist of things is that some condition required interaction with the database. Something like this:

if (someConditionThatRequiresTheDb()) {
  doAThing();
} else {
  doOtherThings();
}

Requires a callback or promise in JavaScript:

someConditionThatRequiresTheDb().then(doAThing);

The mistake here is rejecting the promise when that someCondition is false:

someConditionThatRequiresTheDb(() => {
  // condition met
}, () => {
  // condition not met
});

That’s using exceptions for control flow in a new disguise. Actual errors get eaten by handling the control flow. More importantly, if something did really go wrong, it gets swallowed and evertything chained after the `.then` above continues when it probably shouldn’t! The main advantage of promises is the ability to chain them. Anything that breaks promise chaining and makes it harder to reason about is broken itself.

Instead pass your conditional into the resolved promise.

someConditionThatRequiresTheDb().then(conditionMet => {
  if (conditionMet) {
    doAThing();
  } else {
    doOtherThings();
  }
});

In retrospect, this seems obvious. Sometimes when we’re in the weeds it’s hard to really step back and see common anti patterns when they take on a new guise.