JS illustrated: Promises
Tags: javascript, promises, async
Published: 2019-10-08 14:02:43

This is the second **JS illustrated** article I've wrote. The first one was about the event loop

{% link https://dev.to/kapantzak/js-illustrated-the-event-loop-4mco %}

ES6 ([ECMAScript 2015](https://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects)) has introduced a new feature called *Promise*. There are numerous excelent articles and books that explain the way that Promises work. In this article, we are going to try to provide a simple and understandable description of how Promises work, without digging into much detail.

Before we start explaining what a promise is and how it works, we need to take a look at the reason of its existence, in order to understand it correctly. In other words, we have to identify the problem that this new feature is trying to solve.

# Callbacks

Promises are inextricably linked to [asynchrony](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous). Before Promises, developers were able to write asynchronous code using callbacks. A callback is a function that is provided as parameter to another function, in order to be called, at some point in the future, by the latter function.

Lets take a look at the following code

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=callbacks.js %}

We are calling `ajaxCall` function passing a url path as first argument and a callback function as the second argument. The `ajaxCall` function is supposed to execute a request to the provided url and call the callback function when the response is ready. In the meanwhile, the program continues its execution (the `ajaxCall` does not block the execution). That's an asynchronous piece of code.

This works great! But there are some problems that might arise, like the following ([Kyle Simpson, 2015, You don't know JS: Async & Performance, 42](https://www.amazon.com/You-Dont-Know-JS-Performance/dp/1491904224)):

- The callback function never gets called
- The callback function gets called too early
- The callback function gets called too late
- The callback function gets called more than once

These problems might be more difficult to be solved if the calling function (`ajaxCall`) is an external tool that we are not able to fix or even debug.

> It seems that a serious problem with callbacks is that they give the control of our program execution to the calling function, a state known as [*inversion of control*](https://en.wikipedia.org/wiki/Inversion_of_control) (**IoC**).

The following illustration shows the program flow of a callback based asynchronous task. We assume that we call a third party async function passing a callback as one of its parameters. The red areas indicate that we do not have the control of our program flow in these areas. We do not have access to the third party utility, so the right part of the illustration is red. The red part in the left side of the illustration indicates that we do not have the control of our program until the third party utility calls the callback function we provided.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/rqchyt5pvcnd1cl057el.png)

But wait, there's something else, except from the IoC issue, that makes difficult to write asynchronous code with callbacks. It is known as the [callback hell](http://callbackhell.com/) and describes the state of multiple nested callbacks, as shown in the following snippet.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=callbackHell.js %}

As we can see, multiple nested callbacks makes our code **unreadable** and difficult to debug.

So, in order to recap, the main problems that arise from the use of callbacks are:

- Losing the control of our program execution (Inversion of Control)
- Unreadable code, especially when using multiple nested callbacks

# Promises

Now lets see what Promises are and how they can help us to overcome the problems of callbacks.

According to MDN

> The Promise object represents the **eventual** completion (or failure) of an asynchronous operation, and its resulting value.

and

> A Promise is a **proxy** for a value not necessarily known when the promise is created

What's new here is that asynchronous methods can be called and return something immediately, in contrast to callbacks where you had to pass a callback function and hope that the async function will call it some time in the future.

**But what's that it gets returned?** 

*It is a promise that some time in the future you will get an actual value.* 

For now, you can continue your execution using this promise as a **placeholder of the future value**.

Lets take a look at the constructor

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.js %}

We create a Promise with the `new Promise()` statement, passing a function, called the **executor**. The executor gets called immediately at the time we create the promise, passing two functions as the first two arguments, the **resolve** and the **reject** functions respectively. The executor usually starts the asynchronous operation (the `setTimeout()` function in our example).

The *resolve* function is called when the asynchronous task has been successfully completed its work. We then say that the promise has been **resolved**. Optionally yet very often, we provide the result of the asynchronous task to the resolve function as the first argument.

In the same way, in case where the asynchronous task has failed to execute its assigned task, the *reject* function gets called passing the error message as the first argument and now we say that the promise has been **rejected**.

The next illustration presents the way that promises work. We see that, even if we use a third party utility, we still have the control of our program flow because we, immediately, get back a promise, a placeholder that we can use in place of the actual future value.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/4p38iy08e1zxli9umt1c.png)

According to Promises/A+ specification

> A promise must be in one of three states: pending, fulfilled, or rejected

When a promise is in *pending* state, it can either transition to the *fullfilled* (resolved) or the *rejected* state.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/s7eyl759whe2d15iea81.png)

What's very important here is that, if a promise gets one of the fulfiled or rejected state, **it cannot change its state and value**. This is called **immutable identity** and protects us from unwanted changes in the state that would lead to undescoverable bugs in our code.

### Getting control back

As we saw earlier, when we use callbacks we rely on another piece of code, often writen by a third party, in order to trigger our callback function and continue the execution of the program.

With promises we do not rely on anyone in order to continue our program execution. We have a promise in our hands that we will get an actual value at some point in the future. For now, we can use this promise as a placeholder of our actual value and continue our program execution just as we would do in synchronous programming.

### Readable async code

Promises make our code more readable compared to callbacks (remember the callback hell?). Check out the following snippet:

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=readable.js %}

We can chain multiple promises in a sequential manner and make our code look like synchronous code, avoiding nesting multiple callbacks one inside another.

### Promise API

The `Promise` object exposes a set of static methods that can be called in order to execute specific tasks. We are going to briefly present each on of them with some simple illustrations whenever possible.

#### Promise.reject(reason)

`Promise.reject()` creates an immediately rejected promise and it is a shorthand of the following code:

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.reject.shorthand.js %}

The next snippet shows that `Promise.reject()` returns the same rejected promise with a traditionally constructed promise (`new Promise()`) that gets immediately rejected with the same reason.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.reject.compare.js %}

#### Promise.resolve(value)

`Promise.resolve()` creates an immediately resolved promise with the given value. It is a shorthand of the following code:

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.resolve.shorthand.js %}

Comparing a promise constructed with the `new` keyword and then, immediately resolved with value `1`, to a promise constructed by `Promise.resolve()` with the same value, we see that both of them return identical results.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.resolve.compare.js %}

##### Thenables

According to Promises/A+ specification

> *thenable* is an object or function that defines a `then` method

Lets see a *thenable* in action in the following snippet. We declare the `thenable` object that has a `then` method which immediately calls the second function with the `"Rejected"` value as argument. As we can see, we can call the `then` method of `thenable` object passing two functions the second of which get called with the `"Rejected"` value as the first argument, just like a promise.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=thenable.js %}

But what if we want to use the `catch` method as we do with promises?

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=thenable.error.js %}

Oops! En error indicating that the `thenable` object does not have a `catch` method available occurs! That's normal because that's the case. We have declared a plain object with only one method, `then`, that **happens** to conform, in some degree, to the promises api behaviour. 

> In any case, **it doesn't mean that an object which exposes a `then` method, is a promise object**.

But how can `Promise.resolve()` help with this situation?

`Promise.resolve()` can accept a *thenable* as its argument and then return a promise object. Lets treat our `thenable` object as a promise object.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=thenable.promise.js %}

`Promise.resolve()` can be used as a tool of converting objects to promises.


#### Promise.all(iterable)

`Promise.all()` waits for all promises in the provided iterable to be resolved and, then, returns an array of the values from the resolved promises **in the order they were specified in the iterable**.

In the following example, we declare 3 promises, `p1`, `p2` and `p3` which they all get resolved after a specific amount of time. We intentionaly resolve `p2` before `p1` to demonstrate that the order of the resolved values that get returned, is the order that the promises were declared in the array passed to `Promise.all()`, and not the order that these promises were resolved.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.all.resolve.js %}

In the upcoming illustrations, the green circles indicate that the specific promise has been resolved and the red circles, that the specific promise has been rejected.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/5aum0ohxf7djz97sekez.png)

But what happens if one or more promises get rejected? The promise returned by `Promise.all()` gets rejected with the value of the first promise that got rejected among the promises contained in the iterable.

Even if more than one promises get rejected, the final result is a rejected promise with the value of the **first promise which was rejected**, and not an array of rejection messages.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.all.reject.js %}

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/26ipydz90e2qtquwvh6o.png)


#### Promise.allSettled(iterable)

`Promise.allSettled()` behaves like `Promise.all()` in the sence that it waits far all promises to be fullfiled. The difference is in the outcome.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.allSettled.js %}

As you can see in the above snippet, the promise returned by the `Promise.allSettled()` gets resolved with an array of objects describing the status of the promises that were passed.

#### Promise.race(iterable)

`Promise.race()` waits for the first promise to be resolved or rejected and resolves, or rejects, respectively, the promise returned by `Promise.race()` with the value of that promise.

In the following example, `p2` promise resolved before `p1` got rejected.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.race.resolve.js %}

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/7xdypmymigrdmhzyooem.png)

If we change the delays, and set `p1` to be rejected at 100ms, before `p2` gets resolved, the final promise will be rejected with the respecive message, as shown in the following illustration.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promise.race.reject.js %}

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/qxqjylr65q2alw939jmz.png)

### Promise.prototype methods

We are now going to take a look at some methods exposed by the promise's prototype object. We have already mentioned some of them previously, and now, we are going to take a look at each one of them in more detail.

#### Promise.prototype.then()

We have already used `then()` many times in the previous examples. `then()` is used to handle the settled state of promises. It accepts a resolution handler function as its first parameter and a rejection handler function as its second parameter, and returns a promise.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=then.js %}

The next two illustrations present the way that a `then()` call operates.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ot07iq679691vqrwtdxf.png)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/hvo6z101qxlcthteq6fi.png)

If the resolution handler of a `then()` call of a resolved promise is not a function, then no error is thrown, instead, the promise returned by `then()` carries the resolution value of the previous state.

In the following snippet, `p1` is resolved with value `1`. Calling `then()` with no arguments will return a new promise with `p1` resolved state. Calling `then()` with an `undefined` resolution handler and a valid rejection handler will do the same. Finally, calling `then()` with a valid resolution handler will return the promise's value.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=then.chain.js %}

The same will happen in case that we pass an invalid rejection handler to a `then()` call of a rejected promise.

Lets see the following illustrations that present the flow of promises resolution or rejection using `then()`, assuming that `p1` is a resolved promise with value `1` and `p2` is a rejected promise with reason `"Error"`.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=promises.init.js %}

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ny5qz8z5csi1kniwhkfi.png)

We see that if we don't pass any arguments or if we pass non-function objects as parametes to `then()`, the returned promise keeps the state (`resolved / rejected`) and the value of the initial state without throwing any error.

But what happens if we pass a function that does not return anything? The following illustration shows that in such case, the returned promise gets resolved or rejected with the `undefined` value.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/gskdsc6e77mpkfp0l1my.png)

#### Promise.prototype.catch()

We call `catch()` when we want to handle rejected cases only. `catch()` accepts a rejection handler as a parameter and returns another promise so it can be chained. It is the same as calling `then()`, providing an `undefined` or `null` resolution handler as the first parameter. Lets see the following snippet.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=catch.js %}

In the next illustration we can see the way that `catch()` operates. Notice the second flow where we throw an error inside the resolution handler of the `then()` function and **it never gets caught**. That happens because this is an asynchronous operation and this error wouldn't have been caught even if we had executed this flow inside a `try...catch` block.

On the other hand, the last illustration shows the same case, with an additional `catch()` at the end of the flow, that, actually, catches the error.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/a1o496gxd82cfkkalllj.png)

#### Promise.prototype.finally()

`finally()` can be used when we do not care wheather the promise has been resolved or rejected, just if the promise has been settled. `finally()` accepts a function as its first parameter and returns another promise.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=finally.js %}

The promise which is returned by the `finally()` call is resolved with the resolution value of the initial promise.

{% gist https://gist.github.com/kapantzak/5da8dab3c203b94394c8f3ceee3a3145 file=finally.chain.js %}

# Conclusion

Promises is a wide topic that cannot be fully covered by an article. I've tried to present some simple illustrations that will help the reader to get an idea of the way that promises work in Javascript.

If you find any errors or ommisions, please do not hestitate to mention them! I've put a lot of effort to write this article and I've learned many things about promises. I hope you liked it 😁

# References
- [MDN: Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- [Promises/A+](https://promisesaplus.com/)
- [developers.google](https://developers.google.com/web/fundamentals/primers/promises)
- [Kyle Simpson, 2015, You don't know JS: Async & Performance, 29-119](https://www.amazon.com/You-Dont-Know-JS-Performance/dp/1491904224)

{% youtube Qg1SvpIau6U %}