Rebuilding Promise.all()

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…


This content originally appeared on DEV Community and was authored by Drew Clements

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

// 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.

// 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.

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

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

Let's map through our promises

// 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.

// 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.

// 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.

// 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.

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.


This content originally appeared on DEV Community and was authored by Drew Clements


Print Share Comment Cite Upload Translate Updates
APA

Drew Clements | Sciencx (2021-06-07T12:54:15+00:00) Rebuilding Promise.all(). Retrieved from https://www.scien.cx/2021/06/07/rebuilding-promise-all/

MLA
" » Rebuilding Promise.all()." Drew Clements | Sciencx - Monday June 7, 2021, https://www.scien.cx/2021/06/07/rebuilding-promise-all/
HARVARD
Drew Clements | Sciencx Monday June 7, 2021 » Rebuilding Promise.all()., viewed ,<https://www.scien.cx/2021/06/07/rebuilding-promise-all/>
VANCOUVER
Drew Clements | Sciencx - » Rebuilding Promise.all(). [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/06/07/rebuilding-promise-all/
CHICAGO
" » Rebuilding Promise.all()." Drew Clements | Sciencx - Accessed . https://www.scien.cx/2021/06/07/rebuilding-promise-all/
IEEE
" » Rebuilding Promise.all()." Drew Clements | Sciencx [Online]. Available: https://www.scien.cx/2021/06/07/rebuilding-promise-all/. [Accessed: ]
rf:citation
» Rebuilding Promise.all() | Drew Clements | Sciencx | https://www.scien.cx/2021/06/07/rebuilding-promise-all/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.