解决“竞态问题”
什么是“竞态问题”用户在一个“搜索框”里快速输入或者在“Tab 栏”之间快速切换。前端发出了 请求 A紧接着发出了 请求 B。结果请求 B 先回来了请求 A 因为网络波动慢了一拍最后才回来。灾难页面本该显示 B 的内容最后却被 A 覆盖了。这就叫“竞态问题” (Race Condition)。如何解决使用现代浏览器标准API————AbortControllerAbortController它是Dom标准的一部分专门用来中止一个web请求fetch或者异步操作用法1、创建控制器constcontrollernewAbortController()constsignalcontorller.signal2、发起请求时传入signalfetch(https://api.example.com/data,{signal}).then(resres.json).then(dataconsole.log(data)).catch(err{if(err.nameAbortError){console.log(手动停止)}else{console.log(其他错误操作)}})3、想取消直接调用abort()// 浏览器会直接断开连接Status: canceledcontroller.abort()正确示范封装正确逻辑“只要发新的请求就杀掉旧的”let currentControllernullasync functionhandleSearch(keyword){// 1、清理if(currentController){currentController.abort()}//2、创建新的控制器currentControllernewAbortController()try{constresponseawaitfetch(api/xxx,{signal:currentController.signal})constdataawait response.json()renderList(data)}catch{// 3. 区分是“被取消”还是“真报错”if(err.nameAbortError){console.log(旧请求已丢弃无需处理);return;}console.error(网络错误,err);}finally// 4. 清理可选视逻辑而定if(currentController?.signal.aborted){currentControllernull;}}}进阶 Axios怎么用constcontrollernewAbortController()axios.get(/user/xxxsignal:controller.signal).then(function(response){console.log(response)})controller.abort()封装vue Composable自动管理组件卸载自动取消import{ref,onUnmounted}from vueexprot functionuseFectWithAbort(){constdataref(null)constloadingref(false)constcontroller:AbortController|nullnullconstfetchDataasync(url:string){if(controller)controller.abort()controllernewAbortController()loading.valyetruetry{constresawaitfetch(api/xxx,{signal:controller.signal})data.valueawait res.json()}catch(e:any){if(e.nameAbortController)returnconsole.log(e)}finally{loading.valuefalse}}// 组件卸载时自动取消还在路上的请求防止内存泄漏/报错onUnmounted((){if(controller){controller.abort()}})return{data,loading,fetchData}}结论AbortController不仅是解决bug也是一种节省资源的体现他能真正中断网络传输不仅在js忽略回调也帮用户节省了流量帮服务器节省了带宽