DEV Community

Cover image for Polyfill: Create Promise.five()
TusharShahi
TusharShahi

Posted on • Updated on

Polyfill: Create Promise.five()

Polyfill questions are common in Javascript interviews. Some of the most common ones:

  1. Array.map
  2. Array.reduce
  3. Promise.all
  4. Promise.any

etc.

Some time ago I was asked a to create a Promise.five polyfill, similar to Promise.all but with subtle differences.

This post will be a write up about how one can tackle a similar question when asked in an interview:

Question: Create a function called minPromises which takes in a single number as an argument. Based on that argument it generates a function that can be used as Promise.five() or Promise.three(). Example: Promise.three() is similar to Promise.all(). It takes in a list of promises and returns when at least three promises resolve.

First Thoughts

It looked like we would have to wait for at least min promises to resolve. So a counter is definitely required.

We need to create a higher order function that takes in a number and returns another function. That function can be assigned to a new static property of Promise class like:

Promise.three = {}
Promise.four = {}
Enter fullscreen mode Exit fullscreen mode

Possible doubts that can arise:

  1. What if Promise.four() is used with an array of promises of length 3.

  2. Should the promise reject with reasons for all the promise failures or a common response for failure?

  3. Should the return array length be same? Some promises will not used in the final result. What should be returned in their indices.

My interviewer cleared them out like this:

  1. Reject straight away.
  2. A common Rejection with a sample string will work.
  3. Empty/null/undefined. Anything. But array order should be maintained just like Promise.all.

Initial code:

We start with the inner most function first. We have a min variable. We can loop through the promises. For every resolved promise we increase success counter. For every rejected promise we increase failure counter.

const min = 3;

function minPromise (promises) {

    return new Promise((resolve, reject) => {
        if (min > promises.length) return resolve("error");

        let counter = 0;
        let errCounter = 0;

        promises.forEach((promise) => {
            promise.then((res) => {
           //increase counter
}).catch((res) => {
          //increase errCounter
});
        });

    });

}
Enter fullscreen mode Exit fullscreen mode

Condition checks:

Now, we can put in the code to increase the counter. We should also start forming a result. For that we would need another array. We would also need the correct index of each promise, so we will have to make use of the second parameter of the forEach callback.

const min = 3;

function minPromise (promises) {

    return new Promise((resolve, reject) => {
        if (min > promises.length) return resolve("error");

        let counter = 0;
        let errCounter = 0;
        let resolvedValues = [];
        promises.forEach((promise, index) => {
            promise.then((res) => {
                counter++;
                resolvedValues[index] = res;
                if (counter === min) {
                    resolve(resolvedValues);
                }
            }).catch((res) => {
                errCounter++;
            });
        });

    });

}
Enter fullscreen mode Exit fullscreen mode

Reject case:

When does this promise reject?

  1. When the min is invalid, and
  2. when there are enough rejected promises that counter can never become min
const min = 3;

function minPromise(promises) {

    return new Promise((resolve, reject) => {
        if (min > promises.length) return reject("error");

        let counter = 0;
        let errCounter = 0;
        let resolvedValues = new Array(promises.length);
        promises.forEach((promise, index) => {
            promise.then((res) => {
                counter++;
                resolvedValues[index] = res;
                if (counter === min) {
                    resolve(resolvedValues);
                }
            }).catch((res) => {
                errCounter++;
                if (errCounter > promises.length - min) {
                    reject("error");
                }
            })
        });
    });

}
Enter fullscreen mode Exit fullscreen mode

Note: Had to prepend reject with return otherwise Promise flow will continue. Any flow continues till the end until a return statement is reached or an error is thrown. An SO answer, explaining the same.

Wrapping the whole thing up (Pun intended):

The above code looks good. Now we have to wrap it in a function that takes in min as an input and can generate different versions of this. Using closures this should be easy.

const minPromises = (min) => {

    return function(promises) {

        return new Promise((resolve, reject) => {
            if (min > promises.length) return reject("error");

            let counter = 0;
            let errCounter = 0;
            let resolvedValues = new Array(promises.length);
            promises.forEach((promise, index) => {
                promise.then((res) => {
                    counter++;
                    resolvedValues[index] = res;
                    if (counter === min) {
                        resolve(resolvedValues);
                    }
                }).catch((res) => {
                    errCounter++;
                    if (errCounter > promises.length - min) {
                        reject("error");
                    }
                })
            });
        });

    }
}
Enter fullscreen mode Exit fullscreen mode

Now the above can be used like :

Promise.four = minPromises(4);
Promise.six = minPromises(6);
Enter fullscreen mode Exit fullscreen mode

And that's it. We are done.

Afterthought

The question touched up on a lot of concepts like closure, currying, and of course promises. The main idea is to do some action when the individual promises resolve or reject.

Something like this is definitely easier if you have done the sample Promise.all polyfill. The solution could have used async await too instead of .then/.catch. This is what I could come up with during the interview.

A codepen link

Happy to receive any kind of feedback about this. Do let me know how you would have solve this?

Top comments (0)