Reverse engineering - Understanding Promises in JavaScript
Tags: showdev, tutorial, beginners, javascript
Published: 2019-07-22 18:29:03

---
title: Reverse engineering - Understanding Promises in JavaScript
published: true
description: We will reverse engineer Promises in JavaScript together with some 90s nostalgia
tags: showdev, tutorial, beginners, javascript
cover_image: https://thepracticaldev.s3.amazonaws.com/i/nh4xebdrre2f3wy2q2k4.jpg
canonical_url: https://softchris.github.io/pages/javascript-promises.html
---

Follow me on [Twitter](https://twitter.com/chris_noring), happy to take your suggestions on topics or improvements /Chris

> Here I am again with yet another article on reverse engineering. The idea, just like last time, is to be better at your craft. This time we are looking at Promises, an async construct that enables you to look at the code in a synchronous way. What is this sorcery you ask? Have a read and you'll find out

If you missed my first article on the topic, it's here and it's reverse engineering a unit testing library:

{% link https://dev.to/itnext/reverse-engineering-how-you-can-build-a-test-library-53e3 %}

Back to this article. Promises. The approach we plan to take here is to have a look at the public API of the construct and try to make some educated guesses and then start implementing. We hope to gain some understanding of what goes on under the hood and hopefully smarten up in the process. 

We will cover the following:

- **Why** promises, this is one of the most important questions you need to ask yourself. Why am I learning/reading/using this?  
- **What**, what are core concepts of Promises 
- **Implementation**, we will be implementing a basic Promise but we will also support so-called *chaining*


Ready? 

> All right let's do this.

## WHY

Because Promises are already part of the standard in both JavaScript for the Node.js and the Web means that the word promise is taken, sooo, what's a good synonym? Well, I just took the first thing my brain thought of which was *swear*, which took me all the way back to the year 1994.

Mobile/Cell phones looked like this:

![](https://thepracticaldev.s3.amazonaws.com/i/uleoivv2itwe7ch2nu9m.jpg)

MS-DOS was super popular, everyone was playing the game DOOM and mom was yelling at you for using the Internet when they were trying to use the phone.. ;) 

Sweden scored a Bronze Medal in Football, for all the Brits, this was our 1966.

{% youtube CI5xbXZ4w34  %}


Oh yea All-4-One was topping the charts with "I Swear"

{% youtube 25rL-ooWICU %}

> Yo, I'm here to hear about CODE old man

Yea, sorry. Ok. The great thing about Promises is that they let you arrange code in a way that it looks synchronous while remaining asynchronous. 

> Why is that good?

Consider the alternative *callback hell*, looking like this:

```js
getData((data) => {
  getMoreData(data, (moreData) => {
    getEvenMoreData(moreData, (evenMoreData) => {
      console.log('actually do something')
    })
  })
})
```

3-levels you say, I can maybe live with that. Trust me you don't want to live with 3 or 11 levels. That's why we want Promises.

> Ok ok, show me

With Promises you can write constructs such as this:

```js
getData()
  .then(getMoreData)
  .then(geteEvenMoreData)
```

Seeing that for the first time I was like WOOOW, this changes, everything. I can actually read, line by line, what's happening, no weird tabulation or anything, just read it from the top.

Promises made it into the standard of Web and Node.js and we don't know what we would do without it.

## WHAT

Let's try to establish what we know about Promises so we can recreate it.

So with Promises, we have a way to wrap whatever asynchronous thing we do in a Promise construct like so:

```js
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    // do something 
    resolve('data')
  }, 2000)
})

promise
  .then(
    data => console.log(data)
    err => console.error(err)
  )
  
```

Above we can see that a Promise takes a factory function that has two parameters `resolve` and `reject`, which are both functions. When `resolve` is being invoked the first callback in the `then()` function is being called. Conversely when `reject` is being invoked the second callback in `then` is being called instead and logs it out as an error.

We also support something we've already shown, in the last section called *chaining* which is simply the following:

```js
getData()
  .then(getMoreData)
  .then(geteEvenMoreData)
```

Looking at it from a code standpoint we can see that invoking `then` creates another Promise. We've so far mentioned that it's useful to look at the *asynchronous* code in a *synchronous* looking way but there is more.

Let's make the above example a bit more explicit by creating the functions we mentioned above

```js
function getData() {
  return new Promise((resolve, reject) => {
    resolve('data')
  })
}

function getMoreData(data) {
  return new Promise((resolve, reject) => {
    resolve(data +' more data')
  })
}

function getEvenMoreData(data) {
  return new Promise((resolve, reject) => {
    resolve(data + ' even more data')
  })
}

function getMostData(data) {
  return data + "most";
}

getData()
  .then(getMoreData)
  .then(getEvenMoreData)
  .then(getMostData)
  .then(data => {
    console.log('printing', data)
  })

```

The added strength to our *chaining* is that we can operate on the data we get back and send that right into the next function. So `data` can be sent in as a parameter to `getMoreData()` and the result of that can be sent into the next function and so on. Also, note how we above have a method called `getMostData()`, here we are not even constructing a new Promise but it's enough for us to just return something from the function and it's being resolved. 

Let's mention one more thing before going to implementation, error handling. Now, we've actually shown error handling already:

```js
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    // do something 
    reject('error')
  }, 2000)
})

promise
  .then(
    data => console.log(data)
    err => console.error(err)
  )
```

Calling `reject` causes the second callback in `then` to be invoked. But there is more we could be use something called `catch()`. The idea of the `catch()` is to work as a *catch-all* method. Now, it's important to know how this works. If we already have an error callback on the `then` method, `catch` will not be invoked. So a construct like this wouldn't work as intended:

```js
getData()
  .then(getMoreData, console.error)
  .then(getEvenMoreData)
  .catch(err => console.error)
```

What we want is most likely a scenario that works like this:

1. do call
2. if error, handle by local error handler
3. after the error is handled locally, ensure we short circuit the flow

Implementation would then need to look like this:

```js
getData()
  .then(getMoreData, (err) => {
    // handle error locally
    console.error(err);
    throw new Error(err);
  })
  .then(getEvenMoreData)
  .catch(err => console.error)
```

The above will work as intended if you mean to short circuit it. If you don't implement it like this, the chained promise will actually continue with `getEvenMoreData`. 

That's enough context and insights in how Promises work. Let's try to implement them next.

## Implementation

As I went through this exercise myself. I noticed there was more to Promises than meets the eye.

There's a lot to implementing a Promise
- getting resolve/reject to work + then
- chaining promises
- error handling, both with local error handler but also the catch one
- ensure we handle both the return of promises and simpler objects in a then callback

Given all of the above scenario might easily turn into a 20 min read piece I will try to implement enough to gain valuable insight. 

### Promises construction with resolve/reject

We said we would call it `Swear` once we started implementing it.

Ok then, building time. Let's look at the following construct and let's try to get it to work:

```js
const promise = new Promise((resolve, reject) => {
  resolve('data')
  // reject('error')
})

promise
  then(data => console.log(data));
``` 

We can deduce the following from looking at it that:

- **Is a class**, Promise is a class or at least a constructor function
- **Factory function input**, Promise takes a factory function that has two input parameters `resolve` and `reject`. 
- `resolve` method should trigger `then` callback 

From the above conclusions let's sketch:

```js
// remember, Promise = Swear

class Swear {
  constructor(fn) {
    this.fn = fn;
  }

  _resolve(data) {
    this._fnSuccess(data);
  }

  then(fnSuccess) {
    this._fnSuccess = fnSuccess;
    this.fn(this._resolve.bind(this));
  }
}

const swear = new Swear((resolve) => {
  resolve('data');
})
  .then(data => {
    console.log('swear', data);
  })
```

Running this is in the terminal we get:

![](https://thepracticaldev.s3.amazonaws.com/i/rsmf3ldxn6s6ssslb59m.png)


### Error callback

Ok, so far we've supported `resolve()` that is the *success* scenario in which we use the first callback in a `then()`. Now we are looking to support invoking `reject()` so the following should work:

```js
new Swear((resolve, reject) => {
  reject('err')
})
```

We need to change the code in the following way to make it work:

```js
class Swear {
  constructor(fn) {
    this.fn = fn;
  }

  _resolve(data) {
    this._fnSuccess(data);
  }

  _reject(err) {
    this._fnFailure(err);
  }

  then(fnSuccess) {
    this._fnSuccess = fnSuccess;
    this.fn(this._resolve.bind(this), this._reject.bind(this));
  }
}

const swear = new Swear((resolve) => {
  reject('error');
})
  .then(data => {
    console.log('swear', data);
  }, err => console.error(err))
```

Running the above code you should get a response saying:

```
error error
```

### Chaining
At this point we have a basic construct working. We did it or?

Well, we have ways to go. After this we should be supporting *chaining* meaning we should support that we could write code like this:

```js
const swear = new Swear((resolve) => {
  resolve('data');
})
  .then(data => {
    console.log('swear', data);
    return 'test';
  })
  .then(data => {
    console.log(data)
  })
```

The whole idea with this construct is that we can take the response from one promise and reshape it into something else, like the above where turn `data` into `test`. How to support it though? From the above code we should be producing a `Swear` object when we call `then()` so let's add that part:

```js
class Swear {
  constructor(fn) {
    this.fn = fn;
  }

  _resolve(data) {
    this._fnSuccess(data);
  }

  then(fnSuccess) {
    this._fnSuccess = fnSuccess;
    this.fn(this._resolve.bind(this));

    return new Swear((resolve) => {
      resolve(/* something */)
    })
  }
}
```

Ok, we return the `Swear` instance at the end of `then` but we need to give it some data. Where do we get that data? Actually it comes from invoking `this._fnSuccess`, which we do in `_resolve()`. So let's add some code there:

```js
class Swear {
  constructor(fn) {
    this.fn = fn;
  }

  _resolve(data) {
    this._data = this._fnSuccess(data);
  }

  then(fnSuccess) {
    this._fnSuccess = fnSuccess;
    this.fn(this._resolve.bind(this));

    return new Swear((resolve) => {
      resolve(this._data)
    })
  }
}

swear
.then(data => {
  console.log('swear', data);
  return 'test';
})
.then(data => {
  console.log(data);
})
```

Let's try this code again:

![](https://thepracticaldev.s3.amazonaws.com/i/tnxh0h5ayudz5mii4ibd.png)

We can see above that both of our `.then()` callbacks are being hit.

### Implementing Catch

Catch have the following abilities:

- catch an error, if no `then` error callbacks are specified
- work in conjunction with error callbacks if there is an exception happening inside of a `then` callback.

Where to start? Well adding a `catch()` method is a good start

```js
catch(fnCatch) {
  this._fnCatch = fnCatch;
}
```

Let's think for a second. It should only be called if no other error callbacks have dealt with an error. It should also have knowledge what the error was, regardless of where it happened in the Promise chain.

Looking at how Promise chains seems to work, errors doesn't seem to short-circuit the chain which means if we save the error and pass it on - we should be good. We should also consider having some kind of *handled* concept for when we handle an error. 

Ok then, here is the implementation in all its glory:

```js
class Swear {
  constructor(fn, error = null) {
    this.fn = fn;
    this.handled = false;
    this._error = error;
  }

  _resolve(data) {
    this._data = this._fnSuccess(data);
  }

  _reject(err) {
    this._error = err;
    if(this._fnFailure) {
      this._fnFailure(err);
      this.handled = true;
    }
  }

  then(fnSuccess, fnFailure) {
    this._fnSuccess = fnSuccess;
    this._fnFailure = fnFailure;
    this.fn(this._resolve.bind(this), this._reject.bind(this));

    return new Swear((resolve) => {
      resolve(this._data)
    }, !this.handled ? this._error : null)
  }

  catch(fnCatch) {
    this._fnCatch = fnCatch;
    if (!this.handled && this._error && this._fnCatch) {
      this._fnCatch(this._error);
    }
  }
}

const swear = new Swear((resolve, reject) => {
  reject('error');
})

swear
.then(data => {
  console.log('swear', data);
  return 'test';
} /*, err => console.error('Swear error',err)*/)
.then(data => {
  console.log(data);
})
.catch(err => console.error('Swear, catch all', err));
```

As you can see from the above code, in the `then()` method, we pass the error to the next Promise in the chain IF it has NOT been handled. 

```js
return new Swear((resolve) => {
  resolve(this._data)
}, !this.handled ? this._error : null)
```

We consider an error handled if a local callback takes care of it, as shown in our `_reject()` method:

```js
_reject(err) {
  this._error = err;
  if(this._fnFailure) {
    this._fnFailure(err);
    this.handled = true;
  }
}
```

Lastly, in our `catch()` method, we both receive a callback and invoke said callback, providing the error has NOT been handled, there is an error.

```js
catch(fnCatch) {
  this._fnCatch = fnCatch;
  if (!this.handled && this._error && this._fnCatch) {
    this._fnCatch(this._error);
  }
}
```

We could problaby remove the `_fnCatch()` method and just call `fnCatch` directly. 


**Trying it out**

The big question, does it work?

Well, lets try it out with a local callback and a `catch` method like so:

```js
swear
.then(data => {
  console.log('swear', data);
  return 'test';
} , err => console.error('Swear error',err))
.then(data => {
  console.log(data);
})
.catch(err => console.error('Swear, catch all', err));
```

![](https://thepracticaldev.s3.amazonaws.com/i/tng48str9j487y9cswak.png)

That looks like expected, our local error deals with it and our `catch()` method is never invoked. 

What about no local handlers and just a `catch()` method?

```js
swear
.then(data => {
  console.log('swear', data);
  return 'test';
})
.then(data => {
  console.log(data);
})
.catch(err => console.error('Swear, catch all', err));
```

![](https://thepracticaldev.s3.amazonaws.com/i/54z8ha5cycvznn4exwjq.png)


Let's stop here.. Lots of insight already and let's not make this into a book.

## Summary

In summary, we set out to implement part of a Promise and some abilities on it like resolve/reject, local error handlers, chaining, catch-all. We managed to do so in a few lines but we also realize there are things left to make this work well like being able to the success callback in `then()`
 when it returns a Promise/Swear, raising exceptions in that same callback or a failure callback, handling static methods such Promise.resolve, Promise.reject, Promise.all, Promise.any. Well you get the idea, this is not the end but merely the beginnning

I'm gonna leave you with these parting words from All-4-One

```js
const swear = new Swear((resolve, reject) => {
  resolve('I swear');
})

swear
  .then(data => {
    return `${data}, by the Moon`
  })
  .then(data => {
    return `${data}, and the stars`
  })
  .then(data => {
    return `${data}, and the sun`
  })
  .then(data => console.log(data))
```