To implement promise.all (), we first need to know what the function accepts, does, and returns.

To quote MDN’s description:

The promise.all () method accepts an iterable of promises. Array, Map, and Set are all ES6 iterable types), and only one Promise instance is returned. The result of the resolve callbacks to all of those entered promises is an Array. The Promise’s resolve callback is executed when all the incoming Promise’s resolve callbacks have ended, or when no promises are left in the input iterable. Its Reject callback execution throws an error as soon as any incoming promise’s Reject callback is executed or an invalid promise is entered, and reject is the first error message thrown.

To summarize: Input: an iterable of promises output: a promise, resolve is the result array of all the promise’s resolve callbacks, reject is the error message thrown in the promise or the invalid input.

For example:

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) = > {
  setTimeout(resolve, 100.'foo');
});
const promise4 = new Promise((resolve, reject) = > setTimeout(reject, 101.'reject'))

Promise.all([promise1, promise2, promise3]).then(values= > console.log(values));
Promise.all([promise1, promise4, promise3]).catch(err= > console.log(err));
Promise.all(8).catch(err= > console.log(err));

> TypeError: number 8 is not iterable (cannot read property Symbol(Symbol.iterator))
> Array [3.42."foo"]
> "reject"
Copy the code

Here you can see three lines of output, and you might be confused about the order of output. This involves Javascript event loops. This article only deals with the implementation of promise.all, so we won’t discuss it too much.

Start coding

Start by creating a function that accepts an input argument, Promises, and returns a Promise object

const myPromiseAll = function(promises){
	return new Promise((resolve,reject) = >{})}Copy the code

Next, we’ll start iterating over the variable.

const myPromiseAll = (promises) = > {
  return new Promise((resolve, reject) = > {
    let resolves = []
    const promisesLen = promises.length
    let promisesI = 0
    const next = () = > {
      promises[promisesI].then(res= > {
        resolves.push(res)
        promisesI += 1
        if (promisesI === promisesLen) {
          resolve(resolves) // If the index is out of bounds resolve
        } else{
          next() // Execute iteratively
        }
      }).catch(err= > {
        reject(err) // Error reject
      })
    }
    next()
  })
}
Copy the code

Promises are promises, so promise2 use cases do not pass. We’re going to add some judgment here.

Tip: use the promise instanceof promise to determine if an object is an instanceof a promise

Fix code:

const myPromiseAll = (promises) = > {
  return new Promise((resolve, reject) = > {
    let resolves = []
    const promisesLen = promises.length
    let promisesI = 0
    const next = () = > {
      const nowPormise = promises[promisesI]
      if (nowPormise instanceof Promise) { // Check whether this is a Promise
        nowPormise.then(res= > {
          resolves.push(res)
          promisesI += 1
          if (promisesI === promisesLen) {
            resolve(resolves) // If the index is out of bounds resolve
          }else{
            next() // Execute iteratively
          }
        }).catch(err= > {
          reject(err) // Error reject})}else{
        resolves.push(nowPormise)
        promisesI += 1
        if (promisesI === promisesLen) {
          resolve(resolves)
        }else{
          next()
        }
      }
    }
    next()
  })
}
Copy the code

We also need to determine whether the input is iterable. So how do you tell? TypeError: TypeError: Iterator number 8 is not iterable (cannot read property Symbol(symbol.iterator)) Therefore, we can determine whether an object is iterable by checking whether it has symbol. iterator objects.

  1. Use the in operator
  2. Using hasOwnProperty (Symbol. The iterator)
  3. Array is Object

However, the important thing to note here is that 8 is not an object, 8 is a Number. So, we do two steps to determine whether it is an object and whether it is iterable.

const myPromiseAll = (promises) = > {
  return new Promise((resolve, reject) = > {
    let resolves = []
    const promisesLen = promises.length
    let promisesI = 0
    const next = () = > {
      const promiseType = typeof promises
      if(promiseType === "object") {// Determine whether it is an object
        if(Symbol.iterator in promises){ // Determine whether it is iterable
          const nowPormise = promises[promisesI]
          if (nowPormise instanceof Promise) { // Check whether this is a Promise
            nowPormise.then(res= > {
              resolves.push(res)
              promisesI += 1
              if (promisesI === promisesLen) {
                resolve(resolves) // If the index is out of bounds resolve
              }else{
                next() // Execute iteratively
              }
            }).catch(err= > {
              reject(err) // Error reject})}else{
            resolves.push(nowPormise)
            promisesI += 1
            if (promisesI === promisesLen) {
              resolve(resolves) // If the index is out of bounds resolve
            }else{
              next()
            }
          }
        }else{
          reject(`TypeError: ${promiseType} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`)}}else{
        reject(`TypeError: ${promiseType} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`)
      }
    }
    next()
  })
}
Copy the code

We can beautify the code and rewrite some logic to make the code clearer:

const myPromiseAll = (promises) = > {
  return new Promise((resolve, reject) = > {
    let resolves = []
    const promisesLen = promises.length
    let promisesI = 0
    const check = (res) = > {
      resolves.push(res)
      if (++promisesI === promisesLen) {
        resolve(resolves) // If the index is out of bounds resolve
      } else {
        next() // Execute iteratively}}const next = () = > {
      const promiseType = typeof promises
      if (promiseType === "object" && Symbol.iterator in promises) { // Determine if it is an iterable
        const nowPormise = promises[promisesI]
        if (nowPormise instanceof Promise) { // Check whether this is a Promise
          nowPormise.then(res= > {
            check(res)
          }).catch(err= > {
            reject(err) // Error reject})}else {
          check(nowPormise)
        }
      }
      else {
        reject(`TypeError: ${promiseType} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`)
      }
    }
    next()
  })
}
Copy the code

Test it out:

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) = > {
  setTimeout(resolve, 100.'foo');
});
const promise4 = new Promise((resolve, reject) = > setTimeout(reject, 101.'reject'))
// myPormiseAll ...

myPromiseAll([promise1, promise2, promise3]).then(values= > console.log(values));
myPromiseAll([promise1, promise4, promise3]).catch(err= > console.log(err));
myPromiseAll(8).catch(err= > console.log(err));

Copy the code

Nice!

To sum up:

  1. Need to know what promise.all did
  2. Recursively in then to capture in turn
  3. How do I determine an iterable

Creation is not easy! If it helps you, please also like favorites. If in doubt, leave a comment below. If you have any suggestions, you can send me a private message.