DEV Community

Discussion on: How to Display the Progress of Promises in JavaScript

Collapse
 
lionelrowe profile image
lionel-rowe

This approach works fine if you don't care about any of the results of the promises, but not if you need to use those results (for example, if they're API responses):

const resolveToVal = val => new Promise(res => 
    setTimeout(() => res(val), Math.random() * 1000))

(async() => {
    const max = 5
    let count = 0

    const promises = [...new Array(max)]
        .map((_, i) => resolveToVal(i)
            .then(() => loadingBarStatus(++count, max)))

    const result = await Promise.all(promises)

    console.log(result) // [undefined, undefined, undefined, undefined, undefined]
})()
Enter fullscreen mode Exit fullscreen mode

Instead, you can store the promises to an intermediate variable, then loop over it with forEach for the side-effectey stuff, meanwhile awaiting it with Promise.all so you can do something with the result:

(async() => {
    const max = 5
    let count = 0

    const promises = [...new Array(max)]
        .map((_, i) => resolveToVal(i))

    promises.forEach(promise => promise
        .then(() => loadingBarStatus(++count, max)))

    const result = await Promise.all(promises)

    console.log(result) // [0, 1, 2, 3, 4]
})()
Enter fullscreen mode Exit fullscreen mode

You could further abstract the loading bar logic something like this:

const withLoadingBar = (promises, renderFn, doneMessage) => {
    let count = 0

    promises.forEach(promise => promise
        .then(() =>
            renderFn(`Loading ${++count} of ${promises.length}`)))

    const all = Promise.all(promises)

    all.then(() => renderFn(doneMessage))

    return all
}

(async() => {
    const max = 5

    const promises = [...new Array(max)]
        .map((_, i) => resolveToVal(i))

    const result = await withLoadingBar(promises, console.warn, 'Done!')

    console.log(result)
})()
Enter fullscreen mode Exit fullscreen mode
Collapse
 
zolotarev profile image
Andrew Zolotarev

Too complex.

Collapse
 
shuckster profile image
Conan

Nice! I came up with something Promise-agnostic, although a little more cumbersome to use because of that:

function makeProgressNotifier(options) {
  const {
    min = 0,
    max = 100,
    initialValue,
    onUpdated = () => {},
  } = options || {}

  const range = max - min
  let current = initialValue

  return (getNextValue) => {
    const next = getNextValue(current)
    if (current === next) {
      return
    }
    current = next
    const fraction = (1 / range) * current
    onUpdated({ min, max, current, fraction })
  }
}
Enter fullscreen mode Exit fullscreen mode

Used something like this:

// setup
const update = makeProgressNotifier({
  min: 0,
  max: 100,
  initialValue: 0,
  onUpdated: ({ fraction }) => {
    const percentage = fraction * 100
    console.log('percentage = ', percentage)
  },
})

// progress
const promises = new Array(100).fill(0).map(() => {
  return task().then(() => {
    update((current) => current + 1) // <-- here
  })
})
Enter fullscreen mode Exit fullscreen mode

Thank you OP for the article and video, of course. 🙏