DEV Community

Bryan Ollendyke
Bryan Ollendyke

Posted on

Visualizing Promise Arrays

npm install @lrnwebcomponents/promise-progress --save
Enter fullscreen mode Exit fullscreen mode

The other day I was working with a student who's making a stepped form for a project we're doing. While explaining her UX pattern, she added in a progress bar with a fun visual; assuming we'd be showing them the progress of the site as it's being built.

"Well, the site is literally just copy and paste on the server via a fetch ...." but as I was thinking about it... we could visualize fetch, especially if we had a lot of them. I set to work combining a few things:

Our team loves Lit so we built on top of that a few different implementations. One that's just Lit, one that uses our color library, and one that uses our autoloader registry to simplify references.

Example 1 - basic lit

This is a very simple example that will load 7 Promise responses from an Array of functions

Example 2 - playing with color variables and css

This shows some more CSS work that's inline with what the final thing will be when we use it in our project.

How it works

<progress
  part="progress"
  max="${this.max}"
  value="${this.value}"
></progress>
}
Enter fullscreen mode Exit fullscreen mode

Our render method is excessively simple. All it does is take the max possible and the value. It's a sweet, simple vanilla HTML tag. All the magic is in The Platform (tm) itself.

To obtain and update value and max, we set a list. The list must be an Array of functions that return a Promise. The awesome thing about this is that dynamic import() and fetch() return Promise already! This means that with very little effect and wiring, we have a pathway to display how quickly links are being requested, json data accessed, or dynamic imports are loaded!

In our code pens, we pass in data that looks like this:

[
    () => import("https://unpkg.com/@lrnwebcomponents/meme-maker?module"),
    () => fetch("https://images-api.nasa.gov/search?q=apollo%2011&description=moon%20landing&media_type=image"),
    () => fetch("https://google.com/"),
    () => fetch("https://github.com/"),
]
Enter fullscreen mode Exit fullscreen mode

This shorthand is an array of functions with each function running an import() or fetch(). Another more verbose way could be to write:

[
  function() { return import("https://unpkg.com/@lrnwebcomponents/meme-maker?module");},
Enter fullscreen mode Exit fullscreen mode

This methodology of an array of functions that return a Promise means that we can wire this tag up to any long running task we need to and we'll be updated via Event as to when the series of tasks has completed. It's a great way of visualizing a batch of tasks!

After we have our .list Array populated in the element, it's time to actually process the list. Running .process() when we're ready does all the heavy lifting of Promise resolution by looping through a .map and running each function in a row.

async process() {
    const list = this.list;
    if (this.canLoad) {
      var count = 0;
      const promises = await list.map(async (item) => {
        return await item()
          .then((res) => {
            count = count + 1;
            this.value = Math.round((count / this.list.length) * 100);
            this.loadingBar.textContent = `Loading ${this.value} of ${this.max}`;
            resolve(res);
          })
          .catch((err) => {
            // an error occured
            reject(err);
          });
      });
      await Promise.allSettled(promises).then(() => {
        this.loadingBar.textContent = `Loading Finished`;
        this.value = this.max;
        setTimeout(() => {
          this.dispatchEvent(
            new CustomEvent("promise-progress-finished", {
              detail: {
                value: true,
              },
            })
          );
        }, 100);
      });
    }
Enter fullscreen mode Exit fullscreen mode

The Promise.allSettled allows us to hit 100% each time even if certain parts of the payload failed to deliver / resolve. I went that route because I don't feel it's the responsibility of this to actually handle the error but just to communicate the attempt to complete.

Want to learn more about topics like this in webcomponents? Curious what the HAX team is up to? Join, Learn and Grow together with us May 9/10th in State College for HAXcamp!

Top comments (0)