如何正确的在vue中"同步"使用axios
起因
作为经常经常绑定在一起的Vue和axios,绝大多数人都使用过。这次群里反馈一个有趣的场景:首先ajax请求一个树列表的第一层,然后根据列表的每一项的id去请求下一级的数据赋给当前对象的children;先不讨论这个逻辑设计得合不合理,毕竟存在即合理的。
代码实现类似这样:
async load() {
let data = await axios({
methods: "get",
url: "./json/list.json"
})
data = data.data.data;
data.forEach(async value => {
let res = await axios({
methods: "get",
url: "./json/scroe.json?id=" + value.id
})
value.scroe = res.data.data;
})
this.list = data;
}渲染列表的时候,scroe渲染不出来。
Axios 是一个基于 promise 的 HTTP 库
首先虽然标题是同步使用的axios但是明确得告诉大家,这里得axios请求是异步的,毕竟人家是基于axios的HTTP库,为什么会提供给你一个同步的设置呢,所以不要想找到一个类似jqasync:fasle的配置是没有的。
但是呢,你非要,百度还是会告诉你一个答案滴:
async ()=>{
await axios.get(url,params);
}这个答案是没有问题的,但是需要注意一点是,async/await是让async修饰的函数内部await修饰的promise转化为同步流程执行。
开启分析模式
首先这一堆的async/await确实唬人,所有的请求都同步了,为什么还不行?
async load() {
let data = await axios({
methods: "get",
url: "./json/list.json"
})
data = data.data.data;
data.forEach(async value => {
let res = await axios({
methods: "get",
url: "./json/scroe.json?id=" + value.id
})
value.scroe = res.data.data;
})
this.list = data;
}这段代码,只要赋值的data有值,那么list就可以渲染出结果。所以怀疑这里有两个异步。
问题出在了forEach上 - forEach是一个同步循环调用,它不会等待async函数内的await完成,会直接继续下一次循环。
forEach
javascript的数组循环方法:
[1,2,3].forEach(()=>{})内部实现类似:
Array.prototype.forEach = function(Fn){
var _this = this,
len = _this.length,
params2 = arguments || window;
for( var i = 0 ; i < len; i++ ){
Fn.call(params2,_this[i],i,_this);
}
}虽然async/await能保证传入的函数体内顺序执行,但是forEach这个启动器是一个同步循环调用,不会等待await的异步完成,继续执行下面一次循环。
解决方案
方案一:使用$set
this.$set(value,'scroe',res.data.data)方案二:使用for循环替代forEach
async forEachLoadData(){
for( var i = 0 ,len = data.length; i < len; i++ ){
let res = await axios({
methods: "get",
url: "./json/scroe.json?id=" + data[i].id
})
data[i].scroe = res.data.data;
}
}方案三:使用reduce实现顺序执行
async function load() {
let data = await [1, 2].reduce(async (p, c, index) => {
let data = await p;
return new Promise(resolve=>{
let time = index == 0 ? 5000 : 1000
setTimeout(()=>{
resolve(index )
},time)
})
}, Promise.resolve(0));
console.log('data',data)
}原生数组方法实现
通过上面例子,我们可以发现,我们其实都是通过await和promise交叉来改变函数体内部的逻辑。
类似可以直接使用Promise.all方法处理多个promise。
需要注意的是,需要在回调中返回一个promise。
关于reduce的更多内容可以查看下一张,reduce和中间件。
