Solution

Starting the Function

Step 1: Creating the Structure

We begin by defining our function and returning a new promise. This sets the stage for handling multiple promises.

JAVASCRIPT
1Promise.all = (promises) => {
2  return new Promise((resolve, reject) => {
3    // This is where the magic happens!
4  });
5};

Handling Edge Cases

Step 2: Setting up the Results Array

Before we do anything else, we create an empty array to hold our results.

JAVASCRIPT
1const results = [];

Step 3: No Promises Edge Case

If there are no promises in the input array, we immediately resolve to the empty results array.

JAVASCRIPT
1if (!promises.length) {
2  resolve(results);
3  return;
4}

Resolving the Promises

Step 4: Tracking Pending Promises

We'll set up a counter to keep track of the pending promises. This helps us know when all promises are resolved.

JAVASCRIPT
1let pending = promises.length;

Step 5: Iterating Through the Promises

We'll iterate through the promises, resolving each one.

JAVASCRIPT
1promises.forEach((promise, idx) => {
2  // We'll be resolving each promise here
3});

Step 6: Resolving Each Promise

Inside our loop, we call Promise.resolve on each promise. This takes care of converting non-promise values to promises.

JAVASCRIPT
1Promise.resolve(promise).then((value) => {
2  // We'll handle the resolved value here
3}, reject);

Step 7: Adding Values to Results

When a promise is resolved, we add its value to the results array at the corresponding index. This ensures the order of the results matches the input promises.

JAVASCRIPT
1results[idx] = value;

Step 8: Checking for Completion

We decrement our pending counter and check if it has reached zero. If so, we know all promises are resolved, and we can resolve our main promise with the results array.

JAVASCRIPT
1pending--;
2if (pending === 0) {
3  resolve(results);
4}

Final Code

Putting it all together, our final implementation looks like this:

JAVASCRIPT
1Promise.all = (promises) => {
2  return new Promise((resolve, reject) => {
3    const results = [];
4    if (!promises.length) {
5      resolve(results);
6      return;
7    }
8    let pending = promises.length;
9    promises.forEach((promise, idx) => {
10      Promise.resolve(promise).then((value) => {
11        results[idx] = value;
12        pending--;
13        if (pending === 0) {
14          resolve(results);
15        }
16      }, reject);
17    });
18  });
19};

This step-by-step breakdown of the Promise.all implementation allows us to appreciate the intricacy of asynchronous programming. By understanding how to build such a method ourselves, we gain valuable insight into the nature of promises and how they are handled in JavaScript.