Using promises

What’s a promise?

The natural cure for the callback hell is to start using promises, which allows you to make complex asynchronous operations without having to use callbacks. By definition, promises are the representation of an eventual completion — or deferred result — of some asynchronous operation. What makes it pretty awesome is that the result of a promise might be another promise, hence making it composable. What does this mean in practice? That you can chain your promises (using the then keyword). Another fundamentally nice thing about this is that the code almost looks like a logical sentence.

So, how do we create a promise? A basic and naive implementation could be the following one, where all you have to do is to instanciate a new Promise object, that takes a function as an argument. This function takes two parameters that you can use to either resolve a result once everything is done, or to nicely fail with an error if something unexpected happened.

A simple way to promisify a callback-based function

This is a really simple example, but it points out that you should always catch the potential errors raised along the way–Always keep your promises! In addition, the Promise object also has two static methods used to immediately resolve or reject a single result. This is sometimes useful if you don’t need to wait for an asynchronous outcome to resolve something:

You can also use Promise.resolve and Promise.reject to immediately fulfill your promises

Promises in series

Take a look at the example below, which does exactly what the first callback example did, assuming that all the database functions now return promises:

Simple chain of promises

See? The same logic is much easier to read and reason about. It almost describes what we want to do in a natural language.

The only problem here is that if we wanted to use the user or company variables within the final then statement, we would have to store them in global variables and assign them when we actually find them, in order to be able to use them later. Another approach would be to nest out promises, having to deal with each level of nesting’s errors, like so:

Nested promises in action

Better than callbacks but emh… still nasty, right? We’ll see how to handle this kind of async flow in a few minutes but first, let’s dive into parallel operations flows using promises.

Promises in parallel

Sometimes, we do not need to wait for the outcome of a promise’s to do another unrelated action, even if we need both results to move forward. For instance, imagine a case where we need to create a financial transaction for a given bank account, but we have to check the stock price of the base currency before being able to do so. A way to do this could be to use parallel promises, just like so:

Promises used in parallel

This way, both promises will be launched simultaneously, and we’ll wait for both of their outcomes to carry out the following tasks.

Now we have an overview of how to handle both simple and complex asynchronous flow in series and parallel using promises. Nevertheless, it can still feel tricky to read and understand with a simple glance.