如何解决请求异步的竞态问题?
1、交互层面解决
①、添加全局loading遮罩
②、禁用操作按钮
缺点:
①、阻碍交互
②、触发操作的动作多样,不太好管控
2、取消请求
定义:在每一次请求的时候,取消上一次请求。
2.1、axios的cancellation
let CancelToken=axios.CancelToken()
let source;
/**
提交表单
*/
function submit(params){
if(source){
source.cancel("取消请求)
}
source=CancelToken.source()
return axios.post('/test',{params}
,{
cancelToken:source.token
}).then((respense)=>{
//请求成功处理逻辑
}).catch((thrown)=>{
//区别取消请求和请求失败处理
if(axios.isCancel(thrown){
//取消请求操作逻辑
}else{
//请求错误操作逻辑
}
})
}
2.2、可取消的Promise
注:promise本身是不能取消的,可以通过手动把Promise设置为rejected状态
let doCancel;
function submit(params){
if(doCancel){
// 设置上一次的 Promise 设为 rejected 状态
doCancel("请求取消")
}
return new Promise((resolve,reject)=>{
//挂载reject方法
doCancel=reject
let xhr=new XMLHttpRequest();
xhr.on("load",resolve);
xhr.on("error",reject);
xhr.open("POST","/test",true)
// 发送请求条件,这里未作处理
xhr.send(null)
}).catch((thrown)=>{
// 区别处理取消请求和请求错误
if (axios.isCancel(thrown)) {
// 取消请求的逻辑
} else {
// 请求错误
}
})
}
2.3、RxJs的switchMap 操作符
switchMap 有点类似于Promise。新的数据派发会取消上一次的数据。
var btn = document.querySelector('.js-query');
var inputStream = Rx.Observable.fromEvent(btn, 'click')
.debounceTime(250) // 防抖,防止请求过于频繁
.switchMap(url => Http.get(url))
.subscribe(data => render(data));
3、抛弃无用的请求
最后一种处理方式最为比较容易理解:只处理当前查询条件对应请求结果,其它的查询条件的结果我们都认为是无用的请求,对于无用的请求我们在回调函数里不处理就可以了。
// 请求标记
let gobalReqID = 0
// 请求的函数
funtion query (keyword) {
gobalReqID++
let curReqID = gobalReqID
return axios.post('/list', {
keyword
}).then(res => {
// 对比闭包内的 curReqID 是否和 gobalReqID 一致
if (gobalReqID === curReqID) {
return res
} else {
return Promse.reject('无用的请求')
}
})
}