0

In a Nuxt.js project, I have a page where I need to do some client-side calculations before some data can be displayed in a table. Before, and during the calculation, I want to show a loading screen.

Some context: The data are plots used by a farmer. The table is supposed to show which crop was grown on a plot in the past years. The data is stored in the database is the following way:

plots = [{
  name: 'Plot1',
  year: 2017,
  crop: 'Wheat'
  ...
}, {
  name: 'Plot1',
  year: 2018,
  crop: 'Maize',
  ...
} ...]

In a method that data is converted into a nested Object of the following structure

data = {
  'Plot1': {
    2017: {
      'crop': 'Wheat',
      'catchCrop': true
    },
    2018: {
      'crop': 'Maize',
      'catchCrop': false
    }
  }
  ...
}

and subsequently displayed in the table component.

A mockup of the component looks like the following:

<template>
  <loadingComponent v-if="loading"/>
  <tableComponent v-else-if="!loading && dataAvailable"/>
  <span v-else >No data</span>
</template>
<script>
  data() {
    return {
      loading: true,
      dataAvailable: false
    }
  },
  mounted() {
    this.startCalculation()
  },
  methods: {
    startCalculation() {
      if (store.data) {
        // long running calculation, then
        this.dataAvailable = true
      }
      this.loading = false
    }
  }
</script>

The problem I'm facing is that the loading component never shows. However, the startCalculation method blocks the User Interface (which would be ok if the loading component was shown), and the component is only updated AFTER the calculation is finished.

Does anybody have an idea of how I could circumvent this? Thanks a lot in advance!

EDIT: After fiddling around, I could get it to work the way I want by setting a setTimeout of 1ms. This way, the loading indicator is shown, the data is correctly processed and then the loading indicator is succesfully removed after the calculation finished. However, this feels like a really dirty hack and I would love to avoid it...

mounted() {
  this.loading = true
  // set short timeout in order for Vue to render the loading bar
  setTimeout(() => {
    this.startCalculation()
    this.loading = false
  },1)
}

2 Answers 2

1

For above you need to use async/await as javascript is asynchronous and hence lines are not executed one after the other. That is, if your function is called the engine does not wait for the function to finish its execution but, in parallel, also executes next lines.

Hence, when you use async await on your function the lines are executed one after other. You can also use javascript promises.But if you are calling some method from store than you will need to add async/await over there also.

You can achieve that as below.

async startCalculation() {
      if (store.data) {      
        // long running calculation, then
        await calculationFunction.then(() => {
          this.loading = false
          this.dataAvailable = true
        });
      }
    }
Sign up to request clarification or add additional context in comments.

1 Comment

@Ridddhi, thanks for your comment! I think I need to specify: startCalculation is not an asynchronous method in this case, thus does not return a promise. In other places in the app, e.g. where data is loaded from an external API, it ist really straightforward to show the loading indicator while asynchronously waiting for the data. But this use case is different, as the method in synchronous on purpose.
0

My guess would be to maybe use

`this.$nextTick().then(...DOM should be re-rendered ... do calculations)`

or in a async await style to render the loadingComponent before blocking.

async function(){
    await this.$nextTick()
    // do calculations sync
    // await do calculations async
    // be happy
}

Vue.nextTick Nuxt example

Edit

Alternativly one can just unblock the code. E.g. with the first vue-worker lib found searching:

mounted(){
  this.loading = true;
  async ()=>{
    this.calculatedData= await this.$worker(doBlockingCalculation, ...args);
    this.loading = false;
  }
}

Vue-Worker

2 Comments

Thanks a lot for your answer! Indeed I tried with nextTick, but still the loading component does not show before the calculation is initialized. In the answer above I edited a dirty hack which currently results in the correct behaviour, but seems really bad practice.
This is weird as setTimeout gets called when the stack is emptied same as dom rerendered in this case. But in general try using webworker to outsource blocking code in an own "thread". Then its non blocking ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.