浅谈重复http请求的取消

2022-12-24 0 866

情景:

作为开发人员,他们碰触最少的是CRUD,各种USB的初步设计,但好像会比较少的去关注他们推送的http允诺,当那个允诺没fulfilled,而又有捷伊允诺推送进来,所以现阶段这两个允诺应怎样处理?他们晓得为的是避免多次重复的姿势,他们能采用近似于HDR和IIS来避免出现,但今天他们聊聊从允诺微观上怎样去避免出现多次重复的允诺,而不是从使用者侧去制止。其实网路上也有很多这点的开发人员的试著,但有的是也讲得没所以确切。有鉴于此,在考察了这点的一些科学知识之后,紧密结合自己平常在开发中碰到的情景。归纳出来三个最常用的http允诺须要被中止的情景。

情景一:

完全相同的允诺须要中止,那儿的完全相同的允诺指的是对get 允诺来说,method那样,params那样,url那样,对Post允诺来说,当然是method那样,body那样,url那样

情景二:

路由门牌号发生改变(以后页面的允诺已经无意义)

同时实现方式:

具体来说,要想同时实现多次重复的中止,我们就须要做三步,第二步是具体来说要晓得怎样中止,第三步是怎样推论出现阶段是多次重复的允诺

中止多次重复允诺同时实现方式:

关于怎样中止,该些就要从axios以及fetch三个方面来阐释,即使这三个允诺方式的同时实现中止的方法是有差异的

为的是方便快捷认知,他们通过react来演示那个过程更直接,即使他们晓得钳子表达式useEffect,useEffect的特点决定了回到表达式会在每一场useEffect执行以后,展开一场,来展开清理操作方式,因此在那儿,他们把中止操作方式放到那儿,就能演示每一场都中止前一场的操作方式

axios

具体来说介绍axios,那个大家后端er们肯定碰触得都要吐了,一句话归纳,axios同时实现中止的其本质是采用其外部PCB的cancelToken。

具体来说要晓得,token确认了同一性,这也是确认哪两个允诺须要被中止的标记,它能由cancelToken.source()聚合

source包涵了cancel方式,他们调用它来同时实现中止

useEffect(()=>{

const cancelToken = axios.CancelToken;

const source = cancelToken.source();

setAxiosRes(“axios request created”);

getReq(source).then((res)=>{

setAxiosRes(res);

});

return ()=>{

source.cancel(“axios request cancelled”);

};},[axiosClick]);

export const instance = axios.create({

baseURL:”http://localhost:4001″,

});export const getReq = async (source)=>{ try { const {

data

}= await instance.get(“/”,{

cancelToken: source.token,

}); return data;

} catch (err){ if (axios.isCancel(err)){ return “axios request cancelled”;

} return err;

}

};

那儿须要注意的是,cancel那个姿势,本身是能被catch部分给捕捉到的,也是两个err,他们用它提供的isCancel方式去推论一下是否是中止操作方式,那个能用来验证他们的中止是否成功

fetch:

所以fetch的情况就不那样了,fetch的同时实现方式则是通过signal来中止,其外部的AbortController()能中止掉所有响应signal标记的允诺,同样用react来演示一场,其实其本质还是那样的,并且同样也能被catch到

export const instance = axios.create({ useEffect(()=>{ const controller = new AbortController(); const signal = controller.signal; setFetchRes(“fetch request created”); hitApi(signal).then((res)=>{ setFetchRes(res);

});//cleanup function

return ()=>{

controller.abort();

};

},[fetchClick]);

hitApi表达式如下,也是把signal放进他们的fetch里面,这样他们才能abort。

export const hitApi = async (signal)=>{ try { const response = await fetch(“http://localhost:4001/”,{ signal

}); const data = await response.json(); return data;

} catch (err){ if (err.name ===”AbortError”){ return “Request Aborted “;

} return err;

}

}

那儿同样的,AbortError能在catch被捕获到

推论是否多次重复

好了,中止的功能同时实现了,接下来就要考虑怎样去推论允诺是否多次重复了。那毋庸置疑,推论是否多次重复,想要常数级时间复杂度来找到是否有多次重复的允诺,当然是采用Map,这样能以O(1)的速度找到那个多次重复的允诺,从而才能决定去中止。而且,能想到,整个过程须要向数组里面加东西,而已经被中止过的当然就须要拿出来,因此他们须要两个加的表达式,以及两个移除的表达式

const addPending =(config)=>{ const url =[

config.method,

config.url,

qs.stringify(config.params),

qs.stringify(config.data)

].join(&)

config.cancelToken = config.cancelToken new axios.CancelToken(cancel =>{ if (!pending.has(url)){ // If the current request does not exist in pending, add it

pending.set(url, cancel)

}

})

}

当给 config.cancelToken赋值的时候应注意,现阶段那个 config.cancelToken是否已经有值了

为的是方便快捷他们平铺参数,他们能用qs来转换Object到string

const removePending =(config)=>{ const url =[

config.method,

config.url,

qs.stringify(config.params),

qs.stringify(config.data)

].join(&) if (pending.has(url)){ // If the current request identity exists in pending, you need to cancel the current request and remove it

const cancel = pending.get(url) cancel(url)

pending.delete(url)

}

}

axios拦截器

然而在实际项目中,他们通常有axios拦截器来统一管理他们的允诺,所以那儿也有很多人喜欢直接把这三个方式加进axios拦截器里,这样就一劳永逸了。

axios.interceptors.request.use(config =>{

removePending(options)// Check previous requests to cancel before the request starts

addPending(options)// Add current request to pending

// other code before request

return config

}, error =>{

return Promise.reject(error)

})

axios.interceptors.response.use(response =>{

removePending(response)// Remove this request at the end of the request return response

}, error =>{ if (axios.isCancel(error)){

console.log(repeated request: + error.message)

} else {

// handle error code

} return Promise.reject(error)

})

路由切换采用方式

最后再来说一说第二个情景,路由切换了的情况,比较简单,直接清空他们的pending队列就好,直接怼:

export const clearPending =()=>{ for (const [url, cancel] of pending){

cancel(url)

}

pending.clear()

}

router.beforeEach((to, from, next)=>{

clearPending()

//… next()

})

效果演示:

浅谈重复http请求的取消

能看到,多次重复点击,允诺是cancelled,而如果是成功回到,则是200。

原理分析:

function CancelToken(executor){

if (typeof executor !==function){

throw new TypeError(executor must be a function.);

}

var resolvePromise;

this.promise = new Promise(function promiseExecutor(resolve){

resolvePromise = resolve;//把外部暴露出来

});

var token = this;

//executor(cancel方式);

executor(function cancel(message){

if (token.reason){

// Cancellation has already been requested

return;

}

//token.reason是Cancel的实例

token.reason = new Cancel(message);

resolvePromise(token.reason);//改变promise的状态

});

}

CancelToken的核心其本质上来说其实是将promise挂载上去,然后自己不去主动resolve或者reject,而是把那个主动权先暴露出来,也是代码里的resolvePromise,然后在中止的表达式中去改变那个promise的状态。

改变那个状态有什么用呢,须要紧密结合xhrAdapter源码来认知,在那儿他们就能看到是在什么地方abort的了,标红部分是通过上文改变promise状态,.then里面被执行的过程。

function xhrAdapter(config){

return new Promise(function dispatchXhrRequest(resolve, reject){

if (config.cancelToken){

//允诺中,监听cancelToken中promise状态改变

config.cancelToken.promise.then(function onCanceled(cancel){ if (!request){

return;

}

request.abort();

reject(cancel);

request = null;

});

}

})

}

结语:

其实http允诺中止不是什么很稀奇的事情,要同时实现类似的允诺中止,有很多种其他的方式,甚至很多方式是优于这一种同时实现方式的,比如说取消现阶段那个允诺代替中止前两个,似乎更合乎逻辑一些,但此文着重的是这一种暴露resolve的思想,非常值得一鉴。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务