Promises That Don’t Fail

Introduction

My co-worker, Jason Kaiser, created a way for Promises not to fail, called sureThing. It has 3 benefits to your code that we’ll illustrate below including prior art in other programming languages so you know this isn’t some made-up concept.

What is a SureThing?

A sureThing is a function that wraps a Promise to ensure it never fails. The return value given to the .then is an Object letting you know if the operation succeeded or not.

Returning a resolved Promise in the .catch ensures you prevent the error from propagating, and the Promise becomes resolved instead of rejected, what it normally becomes when a Promise receives an Error or another rejected Promise. Let’s see how this construct can help.

Error Handling

Promise error handling is generally simple: Whether 1 error or 50, nested, or not, synchronous or async, will come out in 1 place inside the .catch callback.

In long Promise chains, however, or those composed of many different promises, while you don’t have to go looking for errors, you certainly do have to figure out “who caused it”, and that isn’t always clear.

The documentsResult and entitlementsResult will tell you if they worked or not by checking the ok boolean.

Another key feature is that the other Promises are not negatively affected by a sibling failing. All are allowed to resolve.

A real-world scenario of this use case is a search I created at work to query 2 different databases. As long as one worked, we were fine to return to the user the results we found. If we used Promise.all with normal Promises, a failing Promise would prevent the successful one from allowing it’s search results being returned the user.

Async Await

Those who like to use asyncawait for making asynchronous code look more imperative and, for them, thusly easier to read. Often upon learning about error handling using this new syntax, they’ll have a sad moment when they learn they must manually include a try/catch in their async function. The pro is, they can be more strategic about where to use the try/catch as you don’t necessarely have to do the whole function like the below code if you want more fine grained errors.

Still, it might be prudent to keep try/catch because if you are writing imperative code like this, you are apt to create exceptions by accident, and the try/catch will keep you safe unlike Go or Lua’s pcall which have facilities to make all functions work like a sureThing. If you wrote this in a normal Promise, you wouldn’t have to worry about it because a Promise has an implicit try/catch.

This style of coding tends to make asyncawait fans very happy to be free of try/catch.

Pure Functions

Promises help encourage pure functions for asynchornous operations. A pure function is a function that will always return the same output with the same inputs and has no side effects. When JavaScript asynchronous first started, callbacks were used. They are noops, meaning a function that returns no value (or undefined). So they aren’t pure and cause side effects on purpose unless you wrap them.

A returned Promise, however, allows a bunch of improvements. First and foremost, same input always results in the same output: an unresolved Promise.

const result = loadEntitlements()
/* result is a Promise */

Since Promises are a data type that wraps a value, but also follows some Monad laws, we can also compose them and use Promises together, like in the case of Promise.all.

Return Value & Prior Art

You see in our sureThing example above we return an Object that has 3 proprties: ok, data, and error. This is just a convention that we follow taking a lead from the Go, Elixir, and Lua developers. It contains the minimum amount of data needed to easily determine if a function worked or not:
– ok saying yes or no
– data containing whatever the Promise resolves to
– error containing helpful information about why the function failed

In longer asyncawaits, you wouldn’t destructure, but in smaller ones you can like so:

Note Elixir uses pattern matching, so this’ll throw an error, which is actually the Erlang way of “let it crash”. A more pure Elixir way would be to always return an Object that follows having nothing for the error so the matching works Elixir:

{:ok, result} = load_entitlements()

Conclusions

As you can see, using sureThings in your code base can help error handling in Promise.all, when using asyncawait functions without a try/catch, and helping ensure you’re creating pure functions.