问题引入
首先提出一个问题:点击页面上一个按钮发送两个ajax请求,其中一个请求会不会等待另一个请求执行完毕之后再执行?
答案是:不会,这两个异步请求会同时发送,至于执行的快与慢,要看响应的数据量的大小及后台逻辑的复杂程度。
从异步请求的执行原理来看,我们知道当一个异步请求发送时,浏览器不会处于锁死、等待的状态,从一个异步请求发送到获取响应结果的期间,浏览器还可以进行其它的操作。这就意味着多个异步请求的执行时并行的。
场景
- 有a、b、c三个异步任务,要求必须先执行a,再执行b,最后执行c
- 且下一次任务必须要拿到上一次任务执行的结果,才能做操作
思路
- 我们需要实现一个队列,将这些异步函数添加进队列并且管理它们的执行,队列具有
First In First Out
的特性,也就是先添加进去的会被先执行,接着才会执行下一个(注意跟栈作区别) - 大家也可以类比一下jQuery的animate方法,添加多个动画也会按顺序执行
解决办法
// 异步函数a
var a = function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve('a')
}, 1000)
})
}
// 异步函数b
var b = function (data) {
return new Promise(function (resolve, reject) {
resolve(data + 'b')
})
}
// 异步函数c
var c = function (data) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(data + 'c')
}, 500)
})
}
解决方法一(使用then链式操作)
特点:可以满足需求,但是书写比较繁琐
//链式调用
a()
.then(function (data) {
return b(data)
})
.then(function (data) {
return c(data)
})
.then(function (data) {
console.log(data)// abc
})
方法二(构建队列)
特点:封装方法,可移植到别处使用
// 构建队列
function queue(arr) {
var sequence = Promise.resolve()
arr.forEach(function (item) {
sequence = sequence.then(item)
})
return sequence
}
// 执行队列
queue([a, b, c])
.then(data => {
console.log(data)// abc
})
方法三(使用async、await构建队列)
特点:同方法二
async function queue(arr) {
let res = null
for (let promise of arr) {
res = await promise(res)
}
return await res
}
queue([a, b, c])
.then(data => {
console.log(data)// abc
})
Promise.reduce也可以用来顺序执行函数,但是可使用的场景非常有限,一般用来读取文件信息,而以上给出的方法,不管你在异步函数中做了什么,只要函数最后返回了一个Promise对象,都可以使用。
promise结合axios在vue中的实例
前台代码
<template>
<div>
<el-button @click="method1()" >method1</el-button>
<el-button @click="method2()" >method2</el-button>
<el-button @click="method3()" >method3</el-button>
<el-button @click="method4()" >method4</el-button>
<el-button @click="method5()" >method5</el-button>
</div>
</template>
<script>
export default {
name:"PromiseTest",
methods:{
method1(){
this.axios.post("/getspringbootproxy/ajax1").then(res => {
console.log("ajax1")
console.log(res)
});
this.axios.post("/getspringbootproxy/ajax2").then(res => {
console.log("ajax2")
console.log(res)
});
},
method2(){
this.axios.post("/getspringbootproxy/ajax1").then(res => {
console.log("ajax1")
console.log(res)
this.axios.post("/getspringbootproxy/ajax2").then(res => {
console.log("ajax2")
console.log(res)
});
});
},
method3(){
this.axios.all([this.method3axios1(), this.method3axios2(),this.method3axios3()])
.then(this.axios.spread(function (res1, res2,res3) {
// 两个请求现在都执行完成
console.log("method3axios1 method3axios2")
console.log(res1)
console.log(res2)
console.log(res3)
}));
},
method3axios1(){
// this.axios.post("/getspringbootproxy/ajax1").then(res => {
// console.log("ajax1")
// console.log(res)
// });
return this.axios.post("/getspringbootproxy/ajax1");
},
method3axios2(){
// this.axios.post("/getspringbootproxy/ajax2").then(res => {
// console.log("ajax2")
// console.log(res)
// });
return this.axios.post("/getspringbootproxy/ajax2");
},
method3axios3(){
// this.axios.post("/getspringbootproxy/ajax2").then(res => {
// console.log("ajax2")
// console.log(res)
// });
return this.axios.post("/getspringbootproxy/ajax3");
},
method4(){
// const p1 = new Promise(function (resolve, reject) {
// setTimeout(() => reject(new Error('fail')), 3000)
// })
// const p2 = new Promise(function (resolve, reject) {
// setTimeout(() => resolve(p1), 1000)
// })
// p2
// .then(result => console.log(result))
// .catch(error => console.log(error))
// Error: fail
new Promise((resolve, reject) => {
return resolve(this.axios.post("/getspringbootproxy/ajax1"));
}).then(r => {
console.log(r)
new Promise((resolve, reject) => {
return resolve(this.axios.post("/getspringbootproxy/ajax2"));
}).then(r => {
console.log(r)
new Promise((resolve, reject) => {
return resolve(this.axios.post("/getspringbootproxy/ajax3"));
}).then(r => {
console.log(r)
});
});
});
},
method5(){
// 因为resolve跟reject这两个参数是可以省略的,所以可以把new Promise(...)写成Promise.resolve或者Promise.reject的形式.
new Promise((resolve, reject) => {
return resolve(this.axios.post("/getspringbootproxy/ajax1"));
}).then(r => {
console.log(r)
return Promise.resolve(this.axios.post("/getspringbootproxy/ajax2"));
})
.then(r => {
console.log(r)
return Promise.resolve(this.axios.post("/getspringbootproxy/ajax3"));
})
.then(r => {
console.log(r)
});
}
}
}
</script>
<style>
</style>
后台代码
@PostMapping("/ajax1")
public Message method7(){
System.out.println("ajax1 5000");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg= new Message<User>();
msg.setCode(1001);
msg.setMsg("ajax1 5000");
return msg;
}
@PostMapping("/ajax2")
public Message method8(){
System.out.println("ajax2 2000");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg= new Message<User>();
msg.setCode(1002);
msg.setMsg("ajax2 2000");
return msg;
}
@PostMapping("/ajax3")
public Message method9(){
System.out.println("ajax3 1000");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg= new Message<User>();
msg.setCode(1003);
msg.setMsg("ajax3 1000");
return msg;
}
结果会发现 前台按钮中
method1 异步请求 按照后台响应时间来调用回调函数
method2 ,method4,method5 按同步顺序执行 ajax1 ajax2 ajax3
method3 异步请求 但是都执行结束后才会执行后续方法
结论
如果想通过同步来控制ajax执行顺序,前一次的执行结果放入到下一个ajax请求中,可以使用method2,4,5。
如果可以通过异步执行ajax但是执行结果不用放到其他ajax请求中,只是再都请求结束后要对执行结果有顺序要求可以使用method3速度比method5 同步执行速度快。
如果无任何顺序要求,仅需要返回结果进行渲染可以用method1。
网友评论