DEV Community

jser
jser

Posted on • Updated on

BFE.dev #32. implement `Promise.all()`

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

Alt Text

This article is about the coding problem BFE.dev#32. 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

function all(promises) {
  return new Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      promise.then((value) => {
        // TODO
      }, (error) => {
        // TODO
      })
    }) 
  })
}
Enter fullscreen mode Exit fullscreen mode

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.

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
      })
    }) 
  })
}
Enter fullscreen mode Exit fullscreen mode

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

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)
      })
    }) 
  })
}
Enter fullscreen mode Exit fullscreen mode

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.

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)
      })
    }) 
  })
}
Enter fullscreen mode Exit fullscreen mode

Passed!

Alt Text

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

Hope it helps, see you next time!

Top comments (0)