BFE.dev #32. implement `Promise.all()`
Tags: javascript
Published: 2020-09-13 14:27:32

> https://bfe.dev is like a LeetCode for FrontEnd developers. I’m using it to practice my skills.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/t915cgki3m9dvh0dfzcf.png)

This article is about the coding problem [BFE.dev#32. implement `Promise.all()`](https://bigfrontend.dev/problem/implement-Promise-all)

## Analysis

`Promise.all()` returns a Promise that

1. fulfills when all promises have fulfilled
2. rejects when any promise is rejected.

So we could chain all the promises with a new fulfill handler and rejection handler:

1. when fulfilled, collect the data and check if ready to fulfill the returned Promise
2. when rejected, reject the returned Promise.


## Let's write some code

Basic skeleton is like this
```js
function all(promises) {
  return new Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      promise.then((value) => {
        // TODO
      }, (error) => {
        // TODO
      })
    }) 
  })
}
```

The fulfilled data should be stored in an array, and the promises might resolve in different order, so `index` is used to set the data at right position.

And the array might have empty holes, so we could use anther variable to keep track of the fulfilled promise count.

```js
function all(promises) {
  return new Promise((resolve, reject) => {
    const result = []
    let fulfilledCount = 0
    
    promises.forEach((promise, index) => {
      promise.then((value) => {
        result[index] = value
        
        fulfilledCount += 1
        if (fulfilledCount === promises.length) {
          resolve(result)
        }
      }, (error) => {
       // TODO
      })
    }) 
  })
}
```

For rejection, once error happens, latter promises no matter fulfilled or rejected, all should be ignored. we could use a flag to indicate this.

```js
function all(promises) {
  return new Promise((resolve, reject) => {
    const result = []
    let fulfilledCount = 0
    let isErrored = false
    
    promises.forEach((promise, index) => {
      promise.then((value) => {
        if (isErrored) return
        result[index] = value
        
        fulfilledCount += 1
        if (fulfilledCount === promises.length) {
          resolve(result)
        }
      }, (error) => {
        if (isErrored) return
        isErrored = true
        reject(error)
      })
    }) 
  })
}
```

The last part is about the input validation, seems like data types other than Promise could be passed in, so we normalize the input and here is the final code.

```js
function all(promises) {
  const _promises = promises.map(
    (item) => item instanceof Promise ? item : Promise.resolve(item)
  )
  
  // resolve if empty 
  if (_promises.length === 0) {
    return Promise.resolve([])
  }
  
  return new Promise((resolve, reject) => {
    const result = []
    let fulfilledCount = 0
    let isErrored = false
    
    _promises.forEach((promise, index) => {
      promise.then((value) => {
        if (isErrored) return
        result[index] = value
        
        fulfilledCount += 1
        if (fulfilledCount === _promises.length) {
          resolve(result)
        }
      }, (error) => {
        if (isErrored) return
        isErrored = true
        reject(error)
      })
    }) 
  })
}
```

## Passed!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qjefeezcm0126j5kgbcp.gif)

If you are interested, have a try at BFE.dev https://bigfrontend.dev/problem/implement-Promise-all

Hope it helps, see you next time!