3

I'm not super versed in JS promises though I generally know enough to be dangerous. I'm working on Vue Method that handles searching a large data object present in the this.data() - Normally when I make asynchronous requests via axios this same formatting works fine but in this case I have to manually create a promise to get the desired behavior. Here is a sample of the code:

        async searchPresets() {

            if (this.presetSearchLoading) {
                return
            }

            this.presetSearchLoading = true; // shows spinner
            this.presetSearchResults = []; // removes old results
            this.selectedPresetImports = []; // removes old user sections from results

            // Need the DOM to update here while waiting for promise to complete
            // otherwise there is no "loading spinner" feedback to the user.
            const results = await new Promise(resolve => {

                let resultSet = [];
                for (var x = 0; x < 10000; x++) {
                    console.log(x);
                }

                let searchResults = [];
                // do search stuff
                resolve(searchResults);

            });

            // stuff after promise

        }

The thing is, the stuff after promise works correctly. It awaits the resolution before executing and receives the proper search result data as it should.

The problem is that the DOM does not update upon dispatching the promise so the UI just sits there.

Does anyone know what I'm doing wrong?

Rick Kukiela
  • 1,135
  • 1
  • 15
  • 34
  • Why do you think that DOM is not getting updated? Is the spinner not showing up while the async promised is waiting to be resolved? Are you sure that the spinner showing logic is working correctly? Maybe the spinner is getting displayed for a quick second as the async promise gets resolved quickly. Can you try adding a timeout to see if that helps? – grane2212 Nov 13 '19 at 00:15
  • Yes I'm sure. I have returned out of the fuction upon setting the variable that shows the spinner and it works. Also I have added that pog loop to extend the duration of the promise showed in my code sample. I can see the logs printing but no spinner. – Rick Kukiela Nov 13 '19 at 00:47
  • What's the "stuff after promise"? That's where you're updating the component's data, right? – Decade Moon Nov 13 '19 at 04:56
  • That's where I would disable the spinner etc. The problem is before this so it's irrelevant. In my test that code is correctly executed after the promise finishes. As I said the problem is the dome does not update at await like it does with axios calls. – Rick Kukiela Nov 13 '19 at 05:40

3 Answers3

3

Try $nextTick():

Vue 2.1.0+:

const results = await this.$nextTick().then(() => {                      
  let resultSet = []
  for (var x = 0; x < 10000; x++) {
    console.log(x)
  }

  let searchResults = []
  // do search stuff
  return searchResults
});

Any Vue:

const results = await new Promise(resolve => {                      
  this.$nextTick(() => {
    let resultSet = []
    for (var x = 0; x < 10000; x++) {
      console.log(x)
    }

    let searchResults = []
    // do search stuff
    resolve(searchResults)
  })
})
BroiSatse
  • 44,031
  • 8
  • 61
  • 86
1

So it turns out I kind of said it all when I said, "I'm not super versed in JS promises though I generally know enough to be dangerous.".

I appreciate the attempts to help me through this but it turns out that making something a promise does not inherently make it asyncronous. This was my mistake. The problem wasn't that Vue was not updating the DOM, the problem was that the promise code was executing synchronously and blocking - thus because execution never actually stopped to await, Vue had no perogative to update the DOM.

Once I wrapped my promise code in a setTimout(() => { /* all code here then: */ resolve(searchResults); }, 200); Everything started working. I guess the set time out allows the execution to stop long enough for vue to change the dom based on my previous data changes. The script still technically blocks the UI while it runs but at least my loader is spinning during this process which is good enough for what I'm doing here.

See: Are JavaScript Promise asynchronous?

Rick Kukiela
  • 1,135
  • 1
  • 15
  • 34
0

Vue will look for data changes and collect them into an array to tell if the DOM needs to be rerendered afterward. This means that everything in Vue is event(data)-driven. Your function only defines behavior that has no data binding to the V-DOM. So the Vue engine will do nothing since nothing in their dependant data set has changed.

I see your Promise function is going to resolve the response to a variable "searchResults". If your DOM uses that variable, the Vue engine will collect the change after the Promise's done. You may put a property in "data()" and bind it to DOM.

For example:

<span v-for="(res, key) in searchResults" :key="key">
  {{ res.name }}
</span>
...
<script>
  export default {
    ...
    data () {
      return { searchResults: [] }
    },
    ...
  }
</script>
daiyanze
  • 103
  • 7