Every Vuex action returns a Promise
.
Vuex wraps the results of the action functions into Promise
s. So the changeColor
action in:
actions: {
changeColor({ commit }, newColor) {
myAsyncCommand();
}
}
Returns a Promise
that resolves to undefined
and that will not wait for the completion myAsyncCommand();
's asynchronous code (if it doesn't contain async code, then there's no waiting to do).
This happens because the code above is the same as:
changeColor({ commit }, newColor) {
myAsyncCommand();
return undefined;
}
And when .dispatch('changeColor', ...)
Vuex will then return Promise.resolve(undefined)
.
If you want the Promise
returned by the action to wait, you should return a Promise
that does the propert waiting yourself. Something along the lines of:
changeColor({ commit }, newColor) {
return new Promise((resolve, reject) => {
myAsyncCommand().then(resolve);
});
// or, simply: return myAsyncCommand();
}
Demo implementation below with more details:
const myStore = {
namespaced: true,
state: { color: "violet" },
mutations: {
changeColor(state, newColor) {
state.color = newColor
}
},
actions: {
changeColor_SIMPLE({ commit }, newColor) {
commit('changeColor', newColor)
},
changeColor_COMPLICATED_NO_PROMISE({ commit }, newColor) {
setTimeout(() => {
commit('changeColor', newColor)
}, 2000)
},
changeColor_COMPLICATED_WITH_PROMISE({ commit }, newColor) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('changeColor', newColor)
resolve();
}, 2000)
});
}
}
};
const store = new Vuex.Store({
modules: {
store: myStore,
}
});
new Vue({
store,
el: '#app',
methods: {
...Vuex.mapActions({
setColorSimple: 'store/changeColor_SIMPLE',
setColorComplicatedNoPromise: 'store/changeColor_COMPLICATED_NO_PROMISE',
setColorComplicatedWithPromise: 'store/changeColor_COMPLICATED_WITH_PROMISE',
}),
myMethodCallByButton(){
this.setColorSimple("blue")
.then(response => console.log("SIMPLE done"),err => console.log("SIMPLE err"));
this.setColorComplicatedNoPromise("blue")
.then(response => console.log("NO_PROMISE done"),err => console.log("NO_PROMISE err"));
this.setColorComplicatedWithPromise("blue")
.then(response => console.log("WITH_PROMISE done"),err => console.log("WITH_PROMISE err"));
}
}
})
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<p>color: {{ $store.state.store.color }}</p>
<button @click="myMethodCallByButton">click me and WAIT for 2s</button>
</div>
Update/Per comments:
Even if the mapAction / dispatch returns a promised, I am in my case obliged to add a promise to wait for the end of the "mutation". I thought, from the documentation, that it was precisely managed via the mapAction. Is it exact?
If an action calls a mutation only, such as:
actions: {
changeColor({ commit }, newColor) {
commit('changeColor', newColor)
return undefined; // added for clarity
}
}
Then the returned Promise
will only execute after the commit()
completes.
That does not happen because Vuex manages waiting of mutations (commit
s).
It happens that way because there's no waiting to do. This is because Vuex requires: mutations must be synchronous operations.
Since mutations are synchronous, the line of the return
above will only execute after the code of the line before (commit('changeColor', newColor)
).
Note: If your mutations have asynchronous code, you should make them synchronous, as it goes against how Vuex properly works and may yield all kinds of unexpected behaviors.