[Vuejs]-Problem with updating a variable in Vue.js

1👍

The problem is that the Firestore query is asynchronous but you aren’t waiting for the response before continuing. Effectively what you have is this:

getDefaultAnswer () {
  var data 

  // This next bit is asynchronous
  db.doLotsOfStuff().then(querySnapshot => {
    // This callback won't have been called by the time the outer function returns
    querySnapshot.forEach(doc => {
      data = doc.data()
    })
  })

  return data
},

The asynchronous call to Firestore will proceed in the background. Meanwhile the rest of the code in getDefaultAnswer will continue to run synchronously.

So at the point the code reaches return data none of the code inside the then callback will have run. You can confirm that by putting in some console logging so you can see what order the code runs in.

The use of then to work with asynchronous code is a feature of Promises. If you aren’t already familiar with Promises then you should study them in detail before going any further. Here is one of the many guides available:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

The bottom line is that you cannot force the getDefaultAnswer method to wait for the asynchronous action to complete. What you can do instead is to return a suitable Promise and then wait for that Promise to resolve before you call onNotSelectedAnswer. It might look something like this:

getDefaultAnswer () {
  // We return the Promise chain from getDefaultAnswer
  return db.doLotsOfStuff().then(querySnapshot => {
    var data = null

    // I have assumed that forEach is synchronous
    querySnapshot.forEach(doc => {
      data = doc.data()
    })

    // This resolves the Promise to the value of data
    return data
  })
},

It is important to appreciate that the method getDefaultAnswer is not attempting to return the value of the data. It is instead returning a Promise that will resolve to the value of the data.

Within CountTerminated you would then use it like this:

this.getDefaultAnswer().then(defaultAnswer => {
  this.onNotSelectedAnswer(defaultAnswer)
})

or if you prefer:

this.getDefaultAnswer().then(this.onNotSelectedAnswer)

The latter is more concise but not necessarily clearer.

You could also write it using async/await but I wouldn’t advise trying to use async/await until you have a solid grasp of how Promises work. While async/await can be very useful for tidying up code it is just a thin wrapper around Promises and you need to understand the Promises to debug any problems.

The code I’ve suggested above should work but there is a delay while it waits for the asynchronous request to complete. In that delay things can happen, such as the user may click on a button. That could get you into further problems.

An alternative would be to load the default answer much sooner. Don’t wait until you actually need it. Perhaps load it as soon as the question is shown instead. Save the result somewhere accessible, maybe in a suitable data property, so that it is available as soon as you need it.

Leave a comment