This is How To Make JS Promises [From Scratch]
Tags: javascript, node, webdev, programming
Published: 2021-08-02 21:35:29

{% 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/
>

---

## Promises, under the hood

---


> _Today, we create our own JavaScript Promise implementation [From Scratch]._

---

To create a new promise we simply use `new Promise` like so:

```js
  new Promise((resolve, reject) => {
    ...
    resolve(someValue)
  })
```

We pass a callback that defines the specific behavior of the promise.


**A promise is a container:**
- Giving us an API to manage and transform a value
- That lets us manage and transform values that are not actually there yet.


Using containers to wrap values is common practice in the functional programming paradigm. There are different kinds of "containers" in functional programming. The most famous being Functors and Monads.

---

## Implementing a promise to understand its internals

---



**1. The `then()` method**

```js
class Promise 
{
   constructor (then) 
   {
      this.then = then
   }
}

const getItems = new Promise((resolve, reject) => {
  HTTP.get('/items', (err, body) => {
    if (err) return reject(err)
    resolve(body)
  })
})

getItems.then(renderItems, console.error)
```

Pretty straight forward, this implementation so far doesn't do anything more than any function with a success (`resolve`) and an error (`reject`) callback.

So check it, when we're making a promise from the ground up we have an extra - normally non-revealed - step to implement.


**2. Mapping**

Currently, our Promise implementation won't work - it's over simplified and doesn't contain all of the required behavior needed to properly work. 

What is one of the features and/or behaviors our implementation is currently missing? 

For starters, we're not able to chain `.then()` calls. 

Promises can chain several `.then()` methods and should return a new Promise each time when the result from anyone of these `.then()` statements is resolved.

 This is one of the primary feature that makes promises so powerful. They help us escape callback hell.

This is also the part of our Promise implementation we are not currently implementing. It can get a bit messy combining all of the functionalities needed to make this Promise chain work properly in our implementation - but we got this.

Let's dive in, simplify, and set up our implementation of a JavaScript Promise to always return or resolve an additional Promise from a `.then()` statement. 

---

To start with, we want a method that will transform the value contained by the promise and give us back a new Promise. 

Hmmm, doesn't this sound oddly familiar? Let's take a closer look. 

Aha, this sounds exactly like how `Array.prototype.map` implements pretty to the mark - doesn't it? 


**`.map`'s type signature is:**

```
map :: (a -> b) -> Array a -> Array b
```

Simplified, this means that map takes a function and transforms type `a` to a type `b`.

This could be a **String** to a **Boolean**, then it would take an Array of **a**(string) and return an Array of **b**(Boolean).

We can build a `Promise.prototype.map` function with a very similar signature to that of `Array.prototype.map` which would allow us to map our resolved promise result into another proceeding Promise. This is how we are able to chain our `.then's` that have callback functions that return any random result, but then seem to magically somehow return Promises without us needing to instantiate any new promises. 


```
map :: (a -> b) -> Promise a -> Promise b
```

**Here's how we implement this magic behind the scenes:**

```js
class Promise 
{
  constructor(then) 
  {
    this.then = then
  }

  map (mapper) 
  {
     return new Promise(
       (resolve, reject) => 
          this.then(x => resolve(mapper(x)), 
          reject
       )
     )
   }
}
```

---

### What'd we just do?

---

Okay, so let's break this down.
- 1. When we create or instanciate a Promise, we are defining a callback that is our then callback aka used when we successfully resolve a result. 

- 2. We create a map function, that accepts a mapper function. This map function returns a new promise. Before it returns a new promise it attempts to resolve the results from the prior promise using. We `map` the results from the prior Promise into a new Promise and then we are back out within the scope of the newly created promise instantiated within our our map method. 

- 3. We can continue this pattern, appending as many `.then` callbacks as we need to and always returning a new Promise without us needing to externally instantiate any new promises outside of our `map` method.


```js
(resolve, reject) => this.then(...))
```

What is happening is that we are calling `this.then` right away. the `this` refers to our current promise, so `this.then` will give us the current inner value of our promise, or the current error if our Promise is failing. We now need to give it a `resolve` and a `reject` callback :

```js
// next resolve =
x => resolve(mapper(x))

// next reject =
reject

```

This is the most important part of our map function. First we are feeding our `mapper` function with our current value `x` :

```js
promise.map(x => x + 1)
// The mapper is actually
x => x + 1
// so when we do
mapper(10)
// it returns 11.
```
And we directly pass this new value (`11` in the example) to the `resolve` function of the new Promise we are creating.

If the Promise is rejected, we simply pass our new reject method without any modification to the value.

```js
  map(mapper) {
    return new Promise((resolve, reject) => this.then(
      x => resolve(mapper(x)),
      reject
    ))
  }
```

```js
const promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve(10), 1000)
})

promise
  .map(x => x + 1)
// => Promise (11)
  .then(x => console.log(x), err => console.error(err))
// => it's going to log '11'
```

To sum it up, what we are doing here is pretty simple. we are just overriding our `resolve` function with a **compositon** of our mapper function and the next `resolve`.
This is going to pass our `x` value to the mapper and resolve the returned value.



---

### Using a bit more of our Promise Implementation:

---

```js
const getItems = new Promise((resolve, reject) => {
  HTTP.get('/items', (err, body) => {
    if (err) return reject(err)
    resolve(body)
  })
})

getItems
  .map(JSON.parse)
  .map(json => json.data)
  .map(items => items.filter(isEven))
  .map(items => items.sort(priceAsc))
  .then(renderPrices, console.error)

```

And like that, we're chaining. Each callback we chain in is a little dead and simple function. 



This is why we love currying in functional programming. Now we can write the following code:

```js
getItems
  .map(JSON.parse)
  .map(prop('data'))
  .map(filter(isEven))
  .map(sort(priceAsc))
  .then(renderPrices, console.error)
```

Arguably, you could say this code is cleaner given you are more familiar with functional syntax. On the other hand, if you're not familiar with functional syntax, then this code made be extremely confusing. 

So, to better understand exactly what we're doing, let's explicitly define how our `.then()` method will get transformed at each `.map` call: 


**Step 1:**

```js
new Promise((resolve, reject) => {
  HTTP.get('/items', (err, body) => {
    if (err) return reject(err)
    resolve(body)
  })
})
```

**Step 2: `.then` is now:**
```js
then = (resolve, reject) => {
  HTTP.get('/items', (err, body) => {
    if (err) return reject(err)
    resolve(body)
  })
}
```

```js
  .map(JSON.parse)
```

**`.then` is now:**
```js
then = (resolve, reject) => {
  HTTP.get('/items', (err, body) => {
    if (err) return reject(err)
    resolve(JSON.parse(body))
  })
}
```

**Step 3:**
```js
  .map(x => x.data)
```

**`.then` is now:**
```js
then = (resolve, reject) => {
  HTTP.get('/items', (err, body) => {
    if (err) return reject(err)
    resolve(JSON.parse(body).data)
  })
}
```

**Step 4:**
```js
  .map(items => items.filter(isEven))
```

**`.then` is now:**
```js
then = (resolve, reject) => {
  HTTP.get('/items', (err, body) => {
    if (err) return reject(err)
    resolve(JSON.parse(body).data.filter(isEven))
  })
}
```

**Step 6:**
```js
  .map(items => items.sort(priceAsc))
```

**`.then` is now:**
```js
then = (resolve, reject) => {
  HTTP.get('/items', (err, body) => {
    if (err) return reject(err)
    resolve(JSON.parse(body).data.filter(isEven).sort(priceAsc))
  })
}
```

**Step 6:**
```js
  .then(renderPrices, console.error)
```

**`.then` is called. The code we execute looks like this:**
```js
HTTP.get('/items', (err, body) => {
  if (err) return console.error(err)
  renderMales(JSON.parse(body).data.filter(isEven).sort(priceAsc))
})
```

---

### 3. Chaining and `flatMap()`

---

Our Promise implementation is still missing something - chaining. 

When you return another promise within the `.then` method, it waits for it to resolve and passes the resolved value to the next `.then` inner function. 

How's this work? In a Promise, `.then` is also flattening this promise container. An Array analogy would be flatMap:


```js
[1, 2, 3, 4, 5].map(x => [x, x + 1])
// => [ [1, 2], [2, 3], [3, 4], [4, 5], [5, 6] ]

[1, 2 , 3, 4, 5].flatMap(x => [x, x + 1])
// => [ 1, 2, 2, 3, 3, 4, 4, 5, 5, 6 ]

getPerson.flatMap(person => getFriends(person))
// => Promise(Promise([Person]))

getPerson.flatMap(person => getFriends(person))
// => Promise([Person])
```


This is our signature breakdown, but if it's tough to follow I'd recommend trying to track down the logic tail a few more times and if it doesn't click then attempt diving into the direct implementation below. We're pretty deep and without experience in functional programming this syntax can be tricky to track, but give it your best go and let's go on in below.

```js

class Promise 
{
  constructor(then) 
  {
    this.then = then
  }

  map(mapper) 
  {
    return new Promise(
      (resolve, reject) => this.then(
         x => resolve(mapper(x)),
         reject
      )
     )
  }

  flatMap(mapper) {
    return new Promise(
      (resolve, reject) => this.then(
         x => mapper(x).then(resolve, reject),
         reject
      )
    )
  }
}

```

We know that `flatMap`'s mapper function will return a Promise. When we get our value x, we call the mapper, and then we forward our resolve and reject functions by calling `.then` on the returned Promise.

```js

getPerson
  .map(JSON.parse)
  .map(x => x.data)
  .flatMap(person => getFriends(person))
  .map(json => json.data)
  .map(friends => friends.filter(isMale))
  .map(friends => friends.sort(ageAsc))
  .then(renderMaleFriends, console.error)

```

How bout that :) 

What we actually did here by separating the differing behaviors of a promise was create a Monad. 

Simply, a monad is a container that implements a `.map` and a `.flatMap` method with these type signatures:
 


```
map :: (a -> b) -> Monad a -> Monad b

flatMap :: (a -> Monad b) -> Monad a -> Monad b
```



The `flatMap` method is also referred as `chain` or `bind`. What we just built is actually called a Task, and the `.then` method is usually named `fork`.
```js

class Task 
{
  constructor(fork) 
  {
    this.fork = fork
  }

  map(mapper) 
  {
    return new Task((resolve, reject) => this.fork(
      x => resolve(mapper(x)),
      reject
    ))
  }

  chain(mapper) 
  {
    return new Task((resolve, reject) => this.fork(
      x => mapper(x).fork(resolve, reject),
      reject
    ))
  }
}

```

The main difference between a Task and a Promise is that a Task is **lazy** and a Promise is not.

What's this mean? 

Since a Task is **lazy** our program won't really execute anything until you call the `fork`/`.then` method.

On a promise, since it is **not lazy**, even when instantiated without its `.then` method never being called, the inner function will still be executed immediately.

By separating the three behaviors characterized by `.then`, making it lazy, 

just by separating the three behaviors of `.then`, and by making it lazy, we have actually implemented in 20 lines of code a [400+ lines polyfill](https://raw.githubusercontent.com/stefanpenner/es6-promise/master/dist/es6-promise.js).

Not bad right?

---

## Summing things up

---

- Promises are containers holding values - just like arrays
- `.then` has three behaviors characterizing it (which is why it can be confusing)
   - `.then` executes the inner callback of the promise immediately
   - `.then` composes a function which takes the future value of the Promises and transforms so that a new Promise containing the transformed value is returned
   - If you return a Promise within a `.then` method, it will treat this similarly to an array within an array and resolve this nesting conflict by flattening the Promises so we no longer have a Promise within a Promise and remove nesting.


---

### Why is this the behavior we want (why is it good?)

---

- Promises compose your functions for you
  - Composition properly separates concerns. It encourages you to code small functions that do only one thing (similarly to the Single Responsibility Principle). Therefore these functions are easy to understand and reuse and can be composed together to make more complex things happen without creating high dependency individual functions.

- Promises abstract away the fact that you are dealing with asynchronous values.

- A Promise is just an object that you can pass around in your code, just like a regular value. This concept of turning a concept (in our case the asynchrony, a computation that can either fail or succeed) into an object is called **reification**.


- It's also a common pattern in functional programming. Monads are actually a reification of some computational context.

---

{% user cleancodestudio %}

[Clean Code Studio](https://cleancode.studio)
[Clean Code](https://cleancode.studio/clean-code)
[JavaScript Algorithms Examples](https://cleancode.studio/algorithms)
[JavaScript Data Structures](https://cleancode.studio/data-structures)

---

{% 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 
> 
>
 
{% codepen https://codepen.io/zhorton34/pen/BaRPYdB %}