Polyfill for Promises
Tags: javascript
Published: 2020-11-08 07:04:53

Please go through the MDN docs for understanding about [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)

Also please note that, don't re-invent the wheel and attempt writing polyfills from scratch by ourselves for a feature which already exists. This is just an illustration of how promise likely works behind the scenes and to imagine ourselves for more understanding.

A sample promise initialisation looks like :

```js
let promise = new Promise((resolve, reject) => setTimeout(() => resolve(1000), 1000));
```
And we specify the tasks to be completed after promise resolution as :

```js
promise.then((val) => console.log(val)).catch(err => console.log(err));
```

Let us implement our polyfill (say `PromisePolyFill` in multiple steps.
From the above codes we know following :

* The promise constructor function must accept a callback as an argument. We will call it as `executor`.
*  It must return an object with at-least two properties , `then` and `catch`
* `then` and `catch` are functions which again accepts a callback and also they can be chained . Hence both must return a reference to `this` 
* We need to store the reference to callback function passed to `then` and `catch` somewhere so that they should be executed at a later point of time , depending on the status of executor. If executor resolved we must invoke the `then` callback . If executor rejects , we must invoke `catch` callback.
* For simplicity , let us assume that our promise will always `resolve`. Hence for now , we will not implement our `catch` functionality , but both `then` and `catch` implementations are exactly identical
* Lets store the callback passed to `then` in a variable named `onResolve`

So our initial code looks like :

```js

function PromisePolyFill(executor) {
   
   let onResolve;

    this.then = function(callback) {
        // TODO: Complete the impl
        onResolve = callback;
        return this;
    };

    this.catch = function(callback) {
        // TODO: We are ignoring this part for simplicity , but its implementation is similar to then
        return this;
    }
}

```
Lets check the `executor` function which we defined initially:

```js
let executor = (resolve, reject) => setTimeout(() => resolve(1000), 1000)
```
This is the callback passed to our promise that we need to execute . Hence we must invoke this executor function which will accept two arguments, `resolve` and `reject`. 

```js

executor(resolve) // reject scenarios ignored for simplicity

```

The executor will either invoke `resolve` or `reject` depending on the status of async operation . For simplicity , we have only considered resolve function here and assume that for now our promise is always resolved.

We now need to define our resolve callback function that is passed as an argument to the executor. Our resolve function is nothing, but just triggers the callback passed to `then` , which we have stored in `onResolve` variable 

```js
    function resolve(val) {

        onResolve(val);
    }
```
We have completed the initial part, of the polyfill.
So as of now our current function looks like this and works perfectly for our base happy-path scenario. We can complete our `catch` functionality similarly. 

```js

function PromisePolyFill(executor) {

    let onResolve;

    function resolve(val) {

        onResolve(val);
    }


    this.then = function(callback) {
        // TODO: Complete the impl
        onResolve = callback;
        return this;
    };

    this.catch = function(callback) {
        // TODO: Complete the impl
        return this;

    }

    executor(resolve);
}

// Sample code for test :
new PromisePolyFill((resolve) => setTimeout(() => resolve(1000), 1000)).then(val => console.log(val));
```

## Part 2

But we have handled only the case where our executor function completed the operation at a later point of time. Lets assume that executor function is synchronous , 

```js
new PromisePolyFill((resolve) => resolve(1000)).then(val => console.log(val));
```
We are likely to encounter this scenario if we directly resolve a variable without any async tasks like `fetch` , `setTimeout` etc
When we invoke our `PromisePolyFill` as above we get an error :

`TypeError: onResolve is not a function`

This happens because our `executor` invocation is completed even before we assign the value of `then` callback to our `onResolve` variable.

So in this case it's not possible for us to execute `onResolve` callback from our `resolve` function . Instead the callback passed to `then` needs to be executed somewhere else.

Now we require two more additional variables :

`fulfilled` :  Boolean indicating if the executor has been resolved or not
`called`: `boolean` indicating if the `then` callback has been `called` or not .

Now our modified implementation looks like :

```js
function PromisePolyFill(executor) {

    let onResolve;
    let fulfilled = false,
    called = false,
    value;


    function resolve(val) {

        fulfilled = true;
        value = val;

        if(typeof onResolve === 'function') {
            onResolve(val);
            called = true; // indicates then callback has been called
        }
    }


    this.then = function(callback) {
        // TODO: Complete the impl
        onResolve = callback;
        return this;
    };

    this.catch = function(callback) {
        // TODO: Complete the impl
        return this;

    }

    executor(resolve);
}

//new PromisePolyFill((resolve) => setTimeout(() => resolve(1000), 0)).then(val => console.log(val));
new PromisePolyFill((resolve) => Promise.resolve(resolve(1000)));


```
This eliminates out `TypeError` , but we still haven't executed our `onResolve` method.
We should do this from out `this.then` initialiser conditionally, if our callback is not called yet and the promise has been fulfilled :

```js
function PromisePolyFill(executor) {
  let onResolve;
  let fulfilled = false,
    called = false,
    value;

  function resolve(val) {
    fulfilled = true;
    value = val;

    if (typeof onResolve === "function") {
      onResolve(val);
      called = true;
    }
  }

  this.then = function (callback) {
    onResolve = callback;

    if (fulfilled && !called) {
      called = true;
      onResolve(value);
    }
    return this;
  };

  this.catch = function (callback) {
    // TODO: Complete the impl
    return this;
  };

  executor(resolve);
}

//new PromisePolyFill((resolve) => setTimeout(() => resolve(1000), 0)).then(val => console.log(val));
new PromisePolyFill((resolve) => resolve(1000)).then(val => console.log(val));

```
With same implementation we can complete our catch code as well. We will have `onReject` callback and `rejected` boolean . Its left out as an exercise :) 

## Part 3 :

Now we shall implement `PromisePolyFill.resolve`, `PromisePolyFill.reject` and `PromisePolyFill.all` just like our `Promise.resolve`, `Promise.reject` and `Promise.all`

`resovle` and `reject` are very straight forward. Here we return a  `PromisePolyFill` object but pass our own executor function which we force to resolve / reject

```js
PromisePolyFill.resolve = (val) =>
  new PromisePolyFill(function executor(resolve, _reject) {
    resolve(val);
  });

PromisePolyFill.reject = (reason) =>
  new PromisePolyFill(function executor(resolve, reject) {
    reject(reason);
  });
``` 

Now lets implement [Promise.all](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). 
It takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises.

```js

PromisePolyFill.all = (promises) => {
  let fulfilledPromises = [],
    result = [];

  function executor(resolve, reject) {
    promises.forEach((promise, index) =>
      promise
        .then((val) => {

          fulfilledPromises.push(true);
          result[index] = val;

          if (fulfilledPromises.length === promises.length) {
            return resolve(result);
          }
        })
        .catch((error) => {
          return reject(error);
        })
    );
  }
  return new PromisePolyFill(executor);
};

```
Here again we create our own executor function, and return back our promise object which would take in this executor.
Our executor function would work as below :

* We maintain an array named `fulfilledPromises` and push values to it whenever any promise is resolved. 
* If all promises are resolved ( `fulfilledPromises.length === promises.length` )  we invoke `resolve` .
* If any promise is rejected we invoke the `reject`

The complete implementation can be found in this gist .

[Github](https://gist.github.com/vijayprwyd/b4085418fb3947076a365056cd77550b)