1

i have a question regarding Javascript Promises in a VueJS setting, i have an application that uses an Action to either fetch a list of Countries from IndexedDB (if it's set ) or from an API by making an Axios HTTP Request.

Now, i'm returning a promise from the action because i want to be able to trigger some popups in the UI when this task is completed, and on top of that both Axios and Dexie(which im using for IndexedDB) run asyncronously through Promises themselves.

  getCountries({commit, dispatch}) {

    commit(types.MUTATIONS.SET_LOADING, true, {root: true})
    commit(types.MUTATIONS.SET_LOADER_MESSAGE, "Loading Countries Data...", {root: true})

    return new Promise((resolve, reject) => {
      db.countries.count().then(value => {
        if(value > 0) {
          console.log("Loading Countries from IndexedDB")
          db.countries.toArray().then(collection => {
            commit(types.MUTATIONS.COUNTRIES.SET, collection, {root: true})
            resolve(collection);
          })
        } else {
          fetchCountriesData().then(data => {
            console.log("Loading Countries from API Call")
            commit(types.MUTATIONS.COUNTRIES.SET, data, {root: true})
            db.countries.bulkAdd(data)
            resolve(data)
          }).catch(error => {
            reject(error)
          })
        }
      })
    })
  }

That is the code for the action, it simply does what i described above, the problem is that this results in an infinite loop where the LOADER Mutations get triggered over and over again.

Why exactly is this going on? Can anyone help me make sense of this? It seems that it runs the initial API action, but THEN after that, with the countries already loaded, it loops and runs again this time invoking the indexeddb mutation as well, which is strange, if i resolve it shouldn't it just end there?

EXTRA::

The action is invoked in a view that i have in my application, i invoke this in the created() hook so that i make sure that the list of countries is always loaded in my Vuex State.

created() {
  this.getAllCountries().then(response => {}).catch(error => {
    snackbar("Unable to load countries!", "error")
  }).then(() => {
    this.setLoadingStatus(false);
  })
}

In this context, it does nothing if it's ok, but that might change in the future, on errors it should show a popup informing the users that the data could not be loaded, and in either case it should hide the loading bar (which is also handled through Vuex)

Could this be the cause of the problem?

JonnySerra
  • 1,044
  • 3
  • 13
  • 27
  • Where are you executing this getCountries function? Can you show us? – GMaiolo Sep 01 '17 at 11:45
  • @GMaiolo will update the question with that info, just a sec – JonnySerra Sep 01 '17 at 11:46
  • @GMaiolo updated the question with the information requested, let me know if you need anything else. – JonnySerra Sep 01 '17 at 11:49
  • the function in the `created` hook is `getAllCountries`, could you also provide the code for that? You may have an infinite loop somewhere there – GMaiolo Sep 01 '17 at 12:04
  • @GMaiolo it's a simple rename using `...mapActions({getAllCountries: 'getCountries'})` That method executes the above action directly. There is no looping done anywhere apart from the Promises which i believe are looping on their own probably because they're not built correctly – JonnySerra Sep 01 '17 at 12:06
  • A hunch... the `created()` method. Do you mean `created : function(){}`? If so, on the line `}).then(() => {`, check that your arrow function is not causing a problem. Arrow functions in vue objects are a bad idea in my experience, and I've reverted to `var me = this;` – bbsimonbb Sep 01 '17 at 12:07
  • 4
    Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Sep 01 '17 at 12:50
  • re the promise antipattern - https://jsfiddle.net/jaromanda/yrbtmobr/ is what your code without the antipattern – Jaromanda X Sep 01 '17 at 13:08
  • I still don't understand what exactly is wrong with doing something like this https://codeshare.io/2p6mE4 It still keeps calling it again and again, my results in the view show the correct array as expected, the problem is that for some reason the promise runs AGAIN after it's been resolved... – JonnySerra Sep 01 '17 at 13:56
  • here's an example of how this could be written taking into account the previous commenters' concerns https://pastebin.com/XjXkcYfd. Note that the db update potentially happens after the action resolves. – Mihail Malostanidis Sep 02 '18 at 10:51

1 Answers1

1

Nevermind, i had a logic error in my code, basically in order to prevent anyone being able to click items while loading i set the view conditionally with a v-if="loading" so that if loading only show the Loader div and otherwise show the actual layout.

The problem with this approach is that it will re-trigger the created hook each time the main view is shown again, thus causing my silly loop.

Hope this helps someone in the future.

JonnySerra
  • 1,044
  • 3
  • 13
  • 27