Displaying a progress bar for a series of promises
Promises are awesome. They make dealing with asynchronous operations in JavaScript a breeze without having to visit or even think about call-back hell. One thing I have often wanted to do is display a progress update for a series of Promises to the UI, not the progress of an individual promise more displaying progress of a set of promises as a whole. Something to display a batch of REST requests to a service to keep track of multiple operations or just a series.
Granted this is an unorthodox situation, which you’d normally just use an indeterminate spinner for
rather than a progress bar. Questions aside how would we display the progress of an array of
(N)
promises to the user?
Typically, you’ll be waiting for a collection of promises to end by using something like Promise. all()
which by design wraps a collection of promises in another that resolves once all of them
are complete and resolved. How can we hijack this a little to get a count of the current status of a
promise? Yes indeed!
For testing, we’ll write a quick function to generate a resolving promise to help us test:
Now using this function we can set-up our Promise.all
block to capture complete progress, but
instead of just passing the promises array, we’re going to pass a slightly adjusted line of
promises that have been mapped adding our handleProgress
function in the then
block.
Any here is a more full example, wrapped in an outer function to scope the doneCount
variables.
The promise.map(p => p.then(handleProgress)
is what adds a promise then
listener
to each promise in our array which we can use to do something after each promise completes
without interrupting the main flow of the core Promise.all
block which will complete if/when
all the promises resolve acting as out “complete” call-back.
Handling errors
The astute readers of the above will notice a large flaw in this approach. Using Promise. all
will make our script reject as soon as one of our Promises in the provided array rejects.
If you are looking to not terminate the entire loading system you can use Promise.allSettled
that will resolve as soon as all the promises have either been fulfilled or rejected, allowing
you to continue the logic as required.
Demo
I’ve spun up a full demo which is available via this CodePen which expands on this additional Promise map idea and adds a bit of CSS and HTML to generate a progress bar.