为什么要用Action管理异步操作
Action 类似于 mutation,不同在于:
1.Action 提交的是 mutation,而不是直接变更状态。
2.Action 可以包含任意异步操作。
官方给的定义我没什么意见,事实上我通过mutation异步操作,好像跟用action管理也没什么区别。关于为什么要用Action管理异步操作,我会通过一个简单的例子和一个复杂的例子来进行说明,事实上,如果初学者没有考虑到实际场景的复杂情况,会觉得Action根本没有一点软用,这个时候就要把问题想得复杂一些了,然后才能看到Action的作用。
先来看一个简单的例子,也是我对如果不用Action进行异步操作的一些初步探索。
第一步:我非常作死的重写了mutation状态管理器中对状态操作的一些写法,我使用了异步操作代替了之前的操作。
// mutation.js
const increment = (state) => {
setTimeout(() => {
state.count++
}, 1000)
}
const decrement = (state) => {
setTimeout(() => {
state.count--
}, 2000)
state.count--
}
export {increment, decrement}
第二步:试验一下能否成功
<template>
<div>
<button @click="decrement">-</button>
<span>{{count}}</span>
<button @click="increment">+</button>
</div>
</template>
<script>
import { mapState, mapMutations} from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['increment', 'decrement']),
}
}
</script>
<style>
</style>
第三步:发现除了每次操作加减时候有一秒的延时,不管你怎么
第四步:用Action处理异步操作(先得把之前作死改掉的mutation的代码改回来)
const increment = (state) => {
state.count++
}
const decrement = (state) => {
state.count--
}
export {increment, decrement}
// action.js处理一些异步操作
// Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象
let incrementAsync = (content) => {
setTimeout(() => {
content.commit('increment')
}, 1000)
}
let decrementAsync = (content) => {
setTimeout(() => {
content.commit('decrement')
}, 1000)
}
export {incrementAsync, decrementAsync}
<template>
<div>
<button @click="decrementAsync">-</button>
<span>{{count}}</span>
<button @click="incrementAsync">+</button>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['increment', 'decrement']),
...mapActions(['incrementAsync', 'decrementAsync']) //这里用了辅助函数,不了解的可以看这个系列的第二篇文章
}
}
</script>
<style>
</style>
第五步:测试一下效果,你会感觉跟没有用action直接用mutation的结果一毛一样。再看一下官网说明。
所以,官网说的这句话有问题?其实官网给了说明。
下面来看一个复杂的例子,这个例子有助于理解为什么要用Action管理异步操作
需求如下
state中存储了一个状态,我们还是复用刚才的count
现在有两个异步操作,他们都能改变count的值
第二个异步操作的条件依赖第一个异步操作的结果,比如第一个异步操作执行了count ++ ,count :0 = >1,第二个异步操作会先判断当前count的值,if(count === 1) { do something...} else { do something... }
当异步操作涉及互相依赖的情况的时候,我们肯定希望被依赖的操作执行完成之后,再执行依赖项,这样能保证程序执行得到正确的结果,但异步操作,如接口访问这种,往往是不能确定执行完成的时间的,
通常你在接口A中得到一个值,a
接口B需要使用这个值结合B接口返回的值进行一些判断操作,if(a&&b){ ... }
这个时候如果B接口执行完毕了,A接口的值还没过来的话,就可能得到错误的结果。 a => undefined
下面我们重写一下代码
// Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象
let incrementAsync = (content) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
content.commit('increment')
resolve()
}, 1000)
})
}
let decrementAsync = (content) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
content.commit('decrement')
resolve()
}, 1000)
})
}
export {incrementAsync, decrementAsync}
<template>
<div>
<button @click="dec">-</button>
<span>{{count}}</span>
<button @click="add">+</button>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['increment', 'decrement']),
...mapActions(['incrementAsync', 'decrementAsync']),
add () {
this.incrementAsync().then(() => {
this.increment()
})
},
dec () {
this.decrementAsync().then(() => {
// do something
})
}
}
}
</script>
<style>
</style>