Implementing a progress loader in percentage by simultaneously running an asynchronous setTimeout counter alongside a Promise.all() operation

I'm encountering a problem running an asynchronous setTimeout counter simultaneously with a Promise.all() handler to display a progress loader in percentage.

Here are the specifics:

I've developed a Vue application comprised of three components.

The first component "Test.vue" is importing a progress bar component and a pivot component that contains around 12 pivot tables with data from different Google Firestore collections (managed via Promise.all()). Currently, it takes approximately 15 seconds for the pivot component to render successfully. During this waiting time, I intend to show a progress bar in percentage that reaches 100% once all data for the pivot component has been loaded and rendered successfully.

I've tried various methods and currently, I am pursuing the "simple" approach of having the progress bar display the loading progress within 15 seconds.

However, even this simple method isn't functioning correctly for me.

It appears to me that the progress functionality always waits until the loading and rendering of the pivot component is completed.

I'm at a loss as to how to resolve this issue.

What recommendations can you provide?

Hint (if relevant): Inside the pivot component, the data is loaded within the mounted-hook using Promise.all()

Below is the code for the Test.vue component:

Test.vue: 

<template>
  <mdb-container class="full-width" style="margin-top:35px !important;">
    <mdb-row v-if="tabChange">
      <mdb-progress :height="30" :value="value">{{value}} %</mdb-progress>
    </mdb-row>
    <mdb-card>
      <mdb-card-header>
        <mdb-tab default class="card-header-tabs">
          <mdb-tab-item 
            v-for="tab in tabs"
            v-bind:key="tab"
            v-bind:class="[{ active: currentTab === tab }]"
            :active="currentTab == tab"
            @click.native.prevent="currentTab=tab"
            >{{ tab }}</mdb-tab-item>
        </mdb-tab>
      </mdb-card-header>
        <mdb-card-body>
          <mdb-tab-content>
            <mdb-tab-pane class="fade">
              <mdb-row>
                <keep-alive>
                  <component v-bind:is="currentTabComponent" v-on:finished="setFinished" class="tab"></component>
                </keep-alive>
              </mdb-row>
            </mdb-tab-pane>
          </mdb-tab-content>
        </mdb-card-body>
    </mdb-card>
  </mdb-container>
</template>

<script>


/* eslint-disable */
import { mdbProgress, mdbContainer,  mdbRow, mdbCol, mdbBtn, mdbCard, mdbCardTitle, mdbCardText, mdbCardFooter, mdbCardBody, mdbCardHeader, mdbListGroup, mdbListGroupItem, mdbNavItem, mdbCardGroup, mdbIcon, mdbFooter, mdbTab, mdbTabItem, mdbTabContent, mdbTabPane } from 'mdbvue';

import { db } from '@/main'
import PivotTable from '@/components/flight-builder/PivotTable'
import DefaultFilters from '@/components/flight-builder/filters/DefaultFilters'
import MainTab from '@/components/flight-builder/ads-archive/MainTab'
import Channel from '@/components/flight-builder/ads-archive/Channel'
import Loader from '@/components/Loader'
//import excel from "vue-excel-export"

/*const Channel = () => ({
  component: import('@/components/flight-builder/ads-archive/Channel'),
  loading: LoadingComponent,
  error: LoadingComponent,
  delay: 200,
  timeout: 3000
})*/

export default {
  name: 'AdsArchivePage',
  components: {
    PivotTable,
    mdbContainer,
    mdbRow,
    mdbCol,
    mdbBtn,
    mdbCard,
    mdbCardTitle,
    mdbCardText,
    mdbCardFooter,
    mdbCardBody,
    mdbCardHeader,
    mdbListGroup,
    mdbListGroupItem,
    mdbNavItem,
    mdbCardGroup,
    mdbIcon,
    mdbFooter,
    mdbTab,
    mdbTabItem,
    mdbTabContent,
    mdbTabPane,
    mdbProgress,
    DefaultFilters,
    MainTab,
    Channel,
    Loader
  },
  data: () => {
    return {
      active: 0,
      currentTab: "Main Tab",
      value: 0,
      tabs: ["Main Tab", "Channel", "Flight", "AdType", "Creative", "Spot length"],
      componentMatcher: {
        "Main Tab": "",
        "Channel": Channel,
        "Flight": "",
        "AdType": "",
        "Creative": "",
        "Spot length": ""
      },
      finishedLoading: false
    }
 
  },
  methods: {
    setFinished(finishedLoading) {
      this.finishedLoading = finishedLoading
    },
    timeout(ms) { //pass a time in milliseconds to this function
        return new Promise(resolve => setTimeout(resolve, ms));
    },
    async wait() {
      let loadingTime = 15
      this.value = 0
      console.log("wait123")
      for (let i = 0; i<=loadingTime; i++) {
          //setTimeout(() => {this.value = Math.round((i / loadingTime)*100)}, 15000);
          this.value = Math.round((i / loadingTime)*100)
          this.$forceUpdate()
          await this.timeout(1000)
          //await sleep(1000);
      }
    }
  },
  computed: {
    currentTabComponent: function() {
      return this.componentMatcher[this.currentTab]
    },
    tabChange: function() {
      if (this.prevTab != this.currentTab) {
        this.wait()
        return true
      }
      return false
    }
  }
}
</script>


Answer №1

While waiting, I aim to display a percentage progress bar that reaches 100% once all data for the pivot component has been loaded and rendered successfully.

I have found that @Keith's solution was effective in showcasing the progress of all promises:

const allProgress = (proms, progress_cb) => {
  let d = 0;
  progress_cb(0); // Start progress_cb
  for (const p of proms) { // Iterate through all promises
    p.then(() => {    
      d++;
      progress_cb((d * 100) / proms.length); // Display each item once it's complete.
    });
  }
  return Promise.all(proms);
}

const test = ms => new Promise((resolve) => {setTimeout(() => resolve(), ms);}); 

allProgress([test(1000), test(2000), test(3000)],
           p => console.log(`% Done = ${p.toFixed(2)}`));

You can customize the progress_cb method to suit your requirements (instead of just using console.log).


Updated

The alternative solution involves using Promise.all. Essentially, it is similar to naortor's approach but with refactored code.

var count = 0;
const waitThenTrigger = p => p.then(val => { progress(++count); return val;}); 
const progress = count =>  console.log(`% Done = ${(count*100/promiseArray.length).toFixed(2)}`);
const createPromise = (ms, value) => new Promise((resolve) => {setTimeout(() => resolve(value), ms);});

var promiseArray = [
  waitThenTrigger(createPromise(3000, "a")),   
  waitThenTrigger(createPromise(2000, "b")),
  waitThenTrigger(createPromise(1000, "c"))];
Promise.all(promiseArray).then(values => console.log(values));

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Styling Javascript Objects

[ [ {"path":"path2","value":"kkk"}, {"path":"path0","value":"uuu"}, {"path":"path1","value":"ppp"} ] ] The result obtained from my manipulation is shown above. However, I require the format to be as depicted below: ["path":"pat ...

Warning of superfluous escape character when executing 'npm start' on a React application

I have encountered a warning while running my React app using npm start. The warning appears in the terminal and reads as follows: Line 24: Unnecessary escape character: \[no-useless-escape The warning is related to the following code snippet: va ...

Having trouble displaying a set of data points in a React component

Exploring React, I have built a test app and performed an API call to fetch a large JSON object. After breaking it down into 10 arrays with 3 props each, I am now facing a challenge in sending this data to another component. Despite being able to log the ...

Issue with Magnific Popup lightbox functionality, and mfp-hide is ineffective

Hello everyone, I am new to stackoverflow so please bear with me as I navigate through it. Although I am still a beginner in HTML, CSS, and JS, I have been using them for work on occasion. Recently, I implemented Magnific Popup on a website I am working o ...

Explaining the implementation of JQuery's onload event and its impact on reducing dependencies

Contained within a table is some data https://i.stack.imgur.com/gQyJQ.png Upon clicking the edit button, I am able to modify the information in that specific row. The Menu Category and Menu are populated through Dependent dropdowns. https://i.stack.imgur ...

Having Trouble Sending Text to InputBox Using Selenium WebDriver

Greetings everyone Can someone guide me on how to use Selenium to input a Login and Password in an Alert Dialog Box? Upon loading the webpage, the alert is already displayed: https://i.stack.imgur.com/F1O5S.png I have attempted the following code: Str ...

The incredible combination of Vue.js with the powerful Hateoas Module

Looking to venture into the world of Vue.js and hoping to utilize it for interacting with a HATEOAS compliant API, yet struggling to find any resources that simplify the process. Are there any modules specifically designed for Vue.js to help consume ...

Connecting to deeply nested attributes within an object using specified criteria

I apologize if the title of my query is not very descriptive, I couldn't come up with a better one. Please feel free to suggest improvements. I am currently working on developing a reusable "property grid" in Angular. My goal is to create a grid wher ...

Error message: "Uncaught TypeError in NextJS caused by issues with UseStates and Array

For quite some time now, I've been facing an issue while attempting to map an array in my NextJS project. The particular error that keeps popping up is: ⨯ src\app\delivery\cart\page.tsx (30:9) @ map ⨯ TypeError: Cannot read pr ...

Incorporate the previous page's location path into the next page using Props in Gatsby Link

My website has multiple pages with paginated lists of blog posts, each post generated from markdown using createPage(). Each page in the /posts directory displays 3 post previews and subsequent pages are numbered (e.g. /posts/2). I am trying to pass the p ...

Having trouble retrieving a value from a .JSON file (likely related to a path issue)

My React component is connected to an API that returns data: class Item extends Component { constructor(props) { super(props); this.state = { output: {} } } componentDidMount() { fetch('http://localhost:3005/products/157963') ...

Using references to pass variables in JavaScript - passing variables to an external test in Mocha

When dealing with primitive datatypes in JavaScript, passing by reference doesn't work. One common workaround is to wrap them in an object. However, what happens if a variable starts as null and then gets reassigned as an Object before being passed to ...

Vue.js | Web scrapers struggle to track v-for generated URLs

I am currently managing a small website that utilizes Laravel and Vue.js to display a list of items. You can check it out here. It seems like the Google crawler is having trouble following the links generated by the v-for function. In my Google Search Con ...

Struggling to integrate a JavaScript sdk with an Angular2 application due to missing dependencies

I've been struggling to incorporate the Magic: The Gathering SDK library into my Angular2 application. I've tried various methods, but nothing seems to work seamlessly. When I attempt to import the library using TypeScript like this: import { } ...

Tips for transferring a boolean value to a generic parameter in Java?

Looking to pass a boolean value to the Generic type in order to be utilized within a condition. This is the generic type interface OptionTypeBase { [key: string]: any; } type OptionsType<OptionType extends OptionTypeBase> = ReadonlyArray<Opt ...

Pattern without anything to duplicate

Could someone help me figure out what I'm doing wrong with this regular expression? I need to create a regex that matches phone numbers in three specific formats: "+38 (093) 937-99-92", "093 937 99 92", and "(093) 937 99 92". As I started working on ...

Is it possible to implement pagination using 'useSWR' in combination with the contentful-client?

I am currently working on implementing pagination in a Next.js project using the useSWR hook. My approach seems to be functioning correctly, but I have a concern about caching due to the key parameter not being a unique string as recommended in the documen ...

Dragging the world map in D3 causes it to appear jumpy and erratic

I'm currently working on a Vue project to create an interactive world map that allows users to drag and zoom. I've attempted to integrate D3 for this purpose, but encountered an issue where the map jumps to the bottom right of the page whenever I ...

What is the reason for Next.js and Gatsby.js necessitating the installation of Node.js, while React.js does not have that requirement?

Have you ever thought about the connection between Next.js and Gatsby.js, both being built on React.js? Could it be that the development environments are Node applications themselves? Or maybe there's something else I'm not seeing? While I have ...

Activate response upon verifying website link

I am adding functionality to my HTML page where I want certain sections to be visible when the user clicks on a link. Initially, these sections are hidden using the CSS property display: none;. My aim is to make these sections visible when the user clicks ...