Rebuilding Promise.all()
Tags: javascript, webdev, codenewbie, programming
Published: 2021-06-07 12:54:15

I was presented with an interesting challenge recently. That challenge was to recreate a method provided by Javascript. Any guesses what that method was? That's right! `Promise.all().`

The purpose of the exercise was to get a deeper understanding of how promises in Javascript work. If you've worked with anything `Promises` or `Async/Await` before, then you know that there are specific things that have to happen in a certain way and order to work- and this is all by design.

## Gathering Context

With that said, we can deduce a few things from `Promise.all()` and use that to scope what our function needs to do.

What do we know about `Promise.all()`? We know it accepts an array of promises. We know it returns an array of what those promises return. We know the returned array is itself a promise. We also know that it only does that last step _if_ all promises succeed- so it errors if any of the passed in promises fail.

Our Definition of Done list could look something like this:
- It accepts an array of promises
- It returns a promise
- It returns an array of those promises' returned value
- It errors if any of the passed in promises fail

## Jumping into the Code

We're only going to be writing a single function for this article. If you want to code along then you can pop open a new index.js file and use your terminal to test it once we're done.

Step one of this is to create the function. Since we're mimic-ing `Promise.all()`, we can call this function `promiseAll`

```javascript
// index.js

function promiseAll() {

}
```

Not too tough, huh? The next step is to let the function know to expect an array of promises when it is called.

```javascript
// index.js

function promiseAll(promises) {

}
```

That's Definition of Done number one off of our list!
- ~~It accepts an array of promises~~
- It returns a promise
- It returns an array of those promises' returned value
- It errors if any of the passed in promises fail

Next we want to set this function up to _return_ a _promise_. 

I highlighted those two words because they almost literally tell us what we need to do next. How do you make a function return? How do you create a new Promise?

If you can answer those two questions, then you already know what our next code snippet should look like.

```javascript
// index.js

function promiseAll(promises) {
  return new Promise((resolve, reject) => {

  })
}
```

See what I mean? We had to `return` a `new Promise`. And that's item two of of our checklist
- ~~It accepts an array of promises~~
- ~~It returns a promise~~
- It returns an array of those promises' returned value
- It errors if any of the passed in promises fail

## Returning an Array of Promises
Number 3 on our checklist is where the difficulty ramps up a bit.

Let's break down what we need.

We need:
- an array we can return
- to get the returned values of the promises passed in

Let's take that one step further. We know we're only going to return the promises' values in an array _if_ they all return successfully.

Knowing that, lets create an array called `successes`

```javascript
// index.js

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    let successes = [];
  })
}
```

Now we need to somehow get the returned values of all promises passed in. Can you think of a couple of ways we can iterate through each promise? 

We can use a for loop or the `.map()` method. Either here would work, but I'm going to use the `.map()` since I'm more familiar with it. Read up on .map() [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)

Let's map through our `promises`

```javascript
// index.js

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    let successes = [];

    promises.map((promise) => {

    }
  })
}
```

Now we can do promise stuff within the scope of each individual promise passed in.

What we'll be doing here is calling each promise individually and using a `.then()` to then get access to its returned value.

We'll also want to add a `.catch` to handle any errors. This actually checks off the fourth thing on our list.

```javascript
// index.js

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    let successes = [];

    promises.map((promise) => {
      return promise.then((res) => {
        
      }.catch((err) => {
        reject(err)
      }
    }
  })
}
```

Remember that our larger function is trying to return an `array` of returned values. Knowing that, we shouldn't immediately resolve our promises.

Instead, we'll push our returned values to our successes array we created earlier.

```javascript
// index.js

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    let successes = [];

    promises.map((promise) => {
      return promise.then((res) => {
        successes.push(res)
      }.catch((err) => {
        reject(err)
      }
    }
  })
}
```

We're getting close!!

Do you know what should happen next? Let's recap.

- Our function is returning a promise.
- Our function is set to error if any of the passed in promises fail.
- And we're pushing our returned values to a `successes` array.

So what's left? _Now_ we need to `resolve` our promise, but there's a condition with it.

We only want to resolve _if_ all passed in promises succeed.

We can do that with an if statement by comparing the length of our successes array to the length of the promises passed in.

```javascript
// index.js

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    let successes = [];

    promises.map((promise) => {
      return promise.then((res) => {
        successes.push(res)

        if(successes.length === promises.length) {
          resolve(successes)
        }
      }.catch((err) => {
        reject(err)
      }
    }
  })
}
```

Now we're making promises we can keep!

Congratulations! You've just rebuilt the functionality of `Promise.all()` and that's the last thing on our list!

- ~~It accepts an array of promises~~
- ~~It returns a promise~~
- ~~It returns an array of those promises' returned value~~
- ~~It errors if any of the passed in promises fail~~

## Running our Promise

Finishing up, let's run our function and see how it works.

Add these to the bottom of your index.js file.

```javascript
const p1 = Promise.resolve(1);
const p2 = new Promise((resolve, reject) => setTimeout(() => resolve(2), 100));
promiseAll([p2, p1]).then((res) => console.log(res));
```

Now, in your terminal-- be sure you're in the right directory- and run `node index.js`.

You should see the fruits of your labor console.logged before you!

### Bonus
There's a small bug in this code. Can you see what it is?

Given the nature of promises, we can assume that there is a good chance that the promises passed in won't return in the same order as when they're passed in.

We're using `.push()` to add our returned value to the successes array. This means that values will always be inserted to the end of the array, so if promise 2 returns before promise 1, it will actually show up in the first index of the successes array.

You can actually see this in our example now.

You would expect to see `[2, 1]` since we passed the args in this order `(P2, P1)`- but they're actually backwards! What we see in the terminal is `[1, 2]`.

This is because P1 resolves immediately, whereas p2 returns a new promise and _then_ resolves.

How would you fix this?

Hint: You _could_ use the index of each passed in promise and then insert their returned value at that index of the successes array.