This is How [JavaScript] Promises Really Work
Tags: javascript, coding, programming, node
Published: 2021-08-02 14:20:33

{% user cleancodestudio %}

[![Twitter Follow](https://img.shields.io/twitter/follow/cleancodestudio.svg?style=social)](https://twitter.com/cleancodestudio) 


>
> Did you know I have a newsletter? 📬
>
> If you want to get notified when I publish new blog posts or
> make major project announcements, head over to 
> https://cleancodestudio.paperform.co/
>


---

## This is How [JavaScript] Promises Really Work

---

> _Promises are one technique to handle asynchronous code,_ 
> _otherwise known as your first class ticket out of callback hell._

---

![JS promise state ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wit653vmh8e2a7wwteum.png)
**3 State's of a Promise**
---
- Pending State
- Resolved State
- Rejected State

---

### Understanding JavaScript Promises

---

What is a promise? 

Commonly, a promise is defined as a proxy for a value that will eventually become available. 



Promises have been a part of JavaScript for years (standardized and introduced in ES2015). More recently, the `async` and `await` keywords (introduced in ES2017) have more deeply integrated and cleaned up the syntax for promises within JavaScript.

**Async functions** use promises behind the scenes, thus - especially with todays distributed cloud architectures becoming more common - understanding what promises are and how they work is more important than ever!

Now that we know promises are important, let's dive in.

---

### How Promises Work (Brief Explanation)

---

Your code calls a promise. This promise will start in what is known as its **pending state**. What's this mean?

This means that the calling function will continue executing while the promise is pending. Once the promise is **resolved** the calling function will get the data that was being requested by the promise. 

A promise starts in a **pending state** and eventually ends in a **resolved state** or a **rejected state**. 

Whether the final outcome be a promise in its **resolved state** or a promise in its **rejected state**, a callback will be called.

We define two separate callbacks. 

One callback handles the data returned from a promise when it ends in a **resolved state**.

The other callback handles the data returned from a promise when it ends in a **rejected state**.

We define the callback function that handles the promise data that ends in a **resolved state** by passing our callback function to **then**.

We define the callback function that handles the promise data  that ends in a **rejected state** by passing our callback function to **catch**.

**Example using axios npm library**

```js
axios.get(endpoint)
     .then(data => resolvedPromiseCallbackFunction(data))
     .catch(errors => rejectedPromiseCallbackFunction(errors))
```


---

### Which JavaScript APIs use promises?

---

Your own code and libraries will most likely use promises throughout. That being noted, promises are actually used by standard modern web APIS. Here's a couple web APIs that also use promises.

- [Service Workers API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
- [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)


In modern JavaScript, it's pretty unlikely you'll find yourself in a situation where you're not using promises - so let's dive deep and start understanding them. 


---

### Creating promises 

---

JavaScript has a Promise API. The Promise API exposes a promise constructor, which you initialize using `new Promise()`:

```js
let complete = true

const hasItCompleted = new Promise((resolve, reject) => {
   if (complete) { 
      const completed = 'Here is the thing I built'
      resolve(completed)
   } else {
     const withReason = 'Still doing something else'
     reject(withReason)
   }
})
```


As shown, we check the `complete` global constant. If `complete` is true, the promise switched to the **resolved** state (aka we call the resolve callback which switches the promise to its **resolved** state). Otherwise, if `complete` is false, the `reject` callback is executed, putting the promise into a **rejected** state. 

Okay - easy enough, if we call the `resolve` callback then our promise switches to the **resolved** state where as if we use the `reject` callback our promise switches to its **rejected** state. That leaves us with a question though.

What if we call neither the `resolve` nor the `reject` callback? Well, as you might be putting together, then the promise remains in its **pending** state. 

Simple enough, three states - two callback functions to switch to **Resolved State** or **Rejected State**, if we call neither callback then we simply remain in the **Pending State**.


---

### Promisifying

---

A more common example that may cross your path is a technique known as **Promisifying**. 

**Promisifying** is a way to be able to use a classic JavaScript function that takes a callback, and have it return a promise:

```js

const fileSystem = require('fs')

const getFile = file => {
    return new Promise((resolve, reject) => {
        fileSystem.readFile(file, (err, data) => {
           if (err) { 
               reject(err)
               return 
           }  

           resolve(data)
        })
    })
}

let file = '/etc/passwd'

getFile(file)
  .then(data => console.log(data))
  .catch(err => console.error(err))
```



> _In recent versions of Node.js, you won't have to do this_ 
> _manual conversion for a lot of the API. There is a_
> _promisifying function available in the util module that will_ > _do this for you, given that the function you're_
> _promisifying has the correct signature._

---

### Consuming A Promise

--- 

Now that understand how a promise can be created using `new Promise()` as well as the **Promisifying** technique, let's talk about _consuming_ a promise. 

> _How do we use a promise (aka how do we consume a promise)_

```js
const isItDoneYet = new Promise(/* ... as above ... */)
//...

const checkIfItsDone = () => {
  isItDoneYet
    .then(ok => {
      console.log(ok)
    })
    .catch(err => {
      console.error(err)
    })
}
``` 

Running `checkIfItsDone()` will specify functions to execute when the `isItDoneYet` promise resolves (in the `then` call) or rejects (in the `catch` call).

---

### Fluently Chaining Promises

---

What if we want to call another promise directly after a previous promise is returned. We can do this, and it's simply called creating a chain of promises.

An example of chaining promises can be found within the Fetch API, which may be used to get a resource and queue (First in First out line) a chain of promises to execute when the resource is fetched.


For starters, let's first point out that the **Fetch API** is a promise-based mechanism. Calling the `fetch()` method is equivalent to defining our own promise using `new Promise()`.


Here's an example of chaining promises fluently together:

```js
const status = response => 
     response.status >= 200 && response.status < 300
          ? Promise.resolve(response)
          : Promise.reject(new Error(response.statusText))   

const json = response => response.json()

fetch('/items.json')
.then(status)
.then(json)
.then(data => console.log('Request success (with json): ', data))
.catch(error => console.log('Request failed: ', error) 

```

> _"[node-fetch](https://www.npmjs.com/package/node-fetch) is minimal code for window.fetch compatible API on Node.js runtime."_

So, what'd we just do?

Well, in the example above we call `fetch()` to get a list of items from the `items.json` file found in the domain root.

Then we create a chaing of promises.

Running `fetch()` returns a [response](https://fetch.spec.whatwg.org/#concept-response). 
- Response contains `status` (numeric HTTP status code)
- Response contains `statusText` (string message, which is `OK` if everything is successful)

`response` also contains a method callable as `json()`. Responses json method returns a promise that will resolve with the content of the body data processed and transformed into `JSON`.

Then we have a final promise in our chain passed in as a anonymous callback function.
```js
data => console.log('Request success (with json): ', data)
```

This function simply logs that we were successful and console logs the successful requests json data.


> _"What if the first promise was rejected though?"

If the first promise would have been rejected, or the second promise, or the third - then, no matter the step, we're automatically going to default to the `catch` callback method that is visually shown at the end of our fluent promise chain. 


---

### Handling Errors

---

We have a promise chain, something fails, uh oh - so what happens?

If anything in the chain of promises fails and raises an error or ultimately sets the promise's state to a **Rejected Promise State**, the control goes directly to the nearest `catch()` statement down our promise chain. 


```js
new Promise((resolve, reject) => {
  throw new Error('Error')
}).catch(err => {
  console.error(err)
})

// or

new Promise((resolve, reject) => {
  reject('Error')
}).catch(err => {
  console.error(err)
})
```


---

### Cascading errors

---

What if we raise an error **inside** a `catch()`? Well, check it - we can simply append a second `catch()`. The second `catch()` will handle the error (or more specifically error message) and so on.

```js
new Promise((resolve, reject) => {
  throw new Error('Error')
})
  .catch(err => {
    throw new Error('Error')
  })
  .catch(err => {
    console.error(err)
  })
```

---

### Promises Orchestration

---

Okay, so now we're solid when it comes to a single promise and our foundational understanding of promises in general.

Getting more advanced, let's ask another question. If you need to synchronize different promises - say pull data from multiple endpoints and handle the resolved promise data from all of the promises created and used to retrieve results from these differing endpoints - how would we do it?

How would we synchronize different promises and execute something when they are all resolved?

**Answer:** `Promise.all()`

`Promise.all()` helps us define a list of promises and execute something when they are all resolved - it allows us to synchronize promises.

`Promise.all()` **Example:**
```js
const one = fetch('/one.json')
const two = fetch('/two.json')

Promise.all([one, two])
  .then(response => console.log('Array of results: ', response)
  .catch(errors => console.error(errors))
```

With destructuring, we can simplify this example to: 

```js
const [one, two] = [fetch('/one.json'), fetch('/two.json')]

Promise.all([one, two])
.then(([resA, resB]) => console.log('results: ', resA, resB))
```

---

### Promise.race()

---

What if we want to get all of the data from these multiple APIs, but we really only need enough data returned from one endpoint to show on our page?

That is we need to resolve all of our promises no matter what, however we want to do something with the data from the first resolved promise and we don't care which promise is resolved first. 

To handle the data from the first resolved promise we can use `Promise.race()`. 

`Promise.race()` runs when the first of the promises you pass to it resolves, and it runs the attached callback just once, with the result of the first promise resolved.

**Example**

```js

const first = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'first')
})
const second = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'second')
})

Promise.race([first, second]).then(result => {
  console.log(result) // second
})
```

---

### Useful Packages Using and/or Simplifying Promises

---

- [Axios (wrapper for fetching data via promises)](https://www.npmjs.com/package//axios)
- [Promise (extends promises)](https://www.promisejs.org/)
- [FS-Extra (uses promises)](https://www.npmjs.com/package/fs-extra)


{% user cleancodestudio %}

>
> Did you know I have a newsletter? 📬
>
> If you want to get notified when I publish new blog posts or
> make major project announcements, head over to 
> https://cleancodestudio.paperform.co/
>

[Clean Code](https://cleancode.studio/clean-code)
[Clean Code Studio](https://cleancode.studio)

Clean Code Studio - Clean Code Clean Life - Simplify!