主题:qklhk-chocolate关于RaceCondition关于RaceCondition,wiki上有详细的介绍(英文版更详细):比如大概是两个线程修改全局资源,理想情况:但是在没有同步锁的情况下,实际情况可能是这样的:如何解决:大体的思路是大部分语言都提供资源锁/同步锁,选择不同的方式来处理这个根据不同语言问题的前端表现是javascript是单线程的,应该不会出现上面的情况。但是在异步渲染的时候,还是会存在渲染时序的问题。表现形式大概是一个detail组件,watch/useEffect传入的id,然后根据id向后端发送请求,然后异步渲染。因为是异步的,不能保证先发送的请求一定是先返回,会出现页面显示的id和详情不匹配的情况:利用同步锁的概念,可以定义一个blockedvariable,在请求的时候,阻止后续请求的发送:看似解决了渲染的时序问题,但是仔细观察会发现这个处理会带来新的问题:整体渲染周期要长很多,前端重在交互和用户界面。这种“锁”也会造成用户操作受阻,对用户使用的影响也不好。场景太单一。比如你在处理输入框智能提示的时序问题,你不能在请求返回之前阻止用户继续输入请求,这样一来即使用户点击了取消,用户还是登录成功。用户切换账号时的场景类似,即在切换成功前取消切换,但切换成功后的操作仍会执行。如果用户没有感知到账号切换,就会出现比较大的bug。切换选项卡/搜索并单击什么字母将返回什么字母。这部分处理了接口。越早的请求响应越慢。当连续点击a->ab->abc时,会出现:firstdisplayabc->ab->a,搜索场景同结果:这个时序问题的原因有很多。简单总结一下包括以下几点:当前网络环境较差且不稳定,无法保证请求返回的稳定性。后端处理逻辑不同。比如两个不同的接口可以触发组件更新,但是后端对这两个接口的处理策略不同,或者这两个接口访问的数据量不同,都会导致请求处理周期不同,也没有保证时序的方法。这时候的用户就倒霉了。第一个请求返回的速度比第二个请求慢。如何解决测试用例?一个简单的Vue组件,根据输入的内容显示不同的结果。这一块的接口做完处理后,最先发送的请求仍然是响应最慢的,会出现查找和结果不匹配的情况。方案一:从底层开始,“取消”请求。目前的请求方式大致有两种:XMLHttpRequest+Fetch,目前主流的方案还是XMLHttpRequest,Fetch因为兼容性问题目前用的还不多。基于XMLHttpRequest,用的最多的大概就是axios了,一般封装了取消请求的方法。我们仍然以Fetch为例。Fetch还是比较尴尬的,它有兼容性问题。和请求控制的AbortController相比,兼容性更差,先不考虑这些。AbortControllerMDN上有详细说明:按照官方的例子即可:asynchandleSearch(){try{this.isCanceled=false;如果(this.controller){this.controller.abort();this.isCanceled=true;}this.controller=newAbortController();const{result}=awaitfetch(`http://localhost:3000/list?search=${this.text}`,{signal:this.controller.signal,}).then((response)=>response.JSON());this.result=结果;console.log("结果",结果);}catch(err){console.log("err",err);//this.controller.signal.abortedif(this.isCanceled){console.log("aborted");}else{this.$message("请求出错");}}}??注意点:cancel请求会去catch,会加上一些异常的场景,所以这块需要单独处理,每次生成一个新的实例。没有找到对应的reset方法报错,获取不到取消请求信息,controller.signal。aborted可以判断请求是否中止,但是因为每次都要生成一个新的实例的原因,只能通过变量来控制,不不不,真的会有人取消请求吗?百度只会保留最新的请求,之前的请求会被取消:谷歌,谷歌最多会保留4个并行请求,然后取消所有之前的请求:奇怪的是没有防抖处理。CancelPromiseCancelPromise,其实就是让Promise提前解决或者拒绝。关于取消的具体姿势,可以看how-to-cancel-your-promise是以下几点:PurePromisesSwitchtogeneratorsNoteonasync/await简单写法:constrequest=(...arg)=>{let取消;constpromise=newPromise((resolve,reject)=>{cancel=()=>reject("aborted");fetch(...arg).then(resolve,reject);});return[promise,cancel];};//...asynchandleSearch(){try{if(this.cancel){this.cancel();}}const[promise,cancel]=request(`http://localhost:3000/list?search=${this.text}`);this.cancel=取消;constresult=(awaitpromise.then((response)=>response.json())).result;this.result=结果;console.log("结果",结果);}catch(err){if(err==="aborted"){console.log(err);}else{this.$message("请求出错");}}}matches只有当当前处理的是请求匹配时才处理请求,否则无所谓。这里有两种情况:有唯一键区分,比如商品详情://ExistsidasynchandleSearch(){try{constdetail=awaitfetch(`xx/${this.id}`);如果(细节.id===this.id){this.detail=细节;}}catch(err){this.$message("请求错误");}}没有唯一键,记录最后一次Promise引用,然后匹配asynchandleSearch(){try{constcurPromise=fetch(`xx/${this.id}`);this.promiseRef=curPromise;constdetail=awaitcurPromise;如果(this.promiseRef===curPromise){this.detail=detail;}}catch(err){this.$message("请求出错");我用过的库,redux-sagaredux-saga,以前用React的时候喜欢用。是Redux的一个中间件,主要处理副作用,也就是请求我感觉这个库实现了一个小型的IO系统。对这部分内容感兴趣的同学可以自行了解。我只谈解决方案。redux-saga提供了TakeLatest的辅助函数来处理这个问题:,]);yieldput({type:'LOAD_STARWARS_HERO_SUCCESS',hero,});}catch(err){yieldput({type:'LOAD_STARWARS_HERO_FAILURE',err,});}},);}rx-jsrx-js是一个响应式库,官方说是异步lodash。将所有数据封装成流进行处理。用到的操作方法主要就是SwitchMap:import{Subject,merge,of}from"rxjs";import{ajax}from"rxjs/ajax";import{switchMap,catchError,tap}from"rxjs/operators";exportdefault{name:"HelloWorld",data(){return{text:"",result:"holder",};},mounted(){this.subject=newSubject();this.subject.pipe(tap(()=>{console.log("text:",this.text);}),switchMap((str)=>ajax(`http://localhost:3000/list?search=${this.text}`)),catchError((err,caught$)=>{returnmerge(of({err}),caught$);})).subscribe((response)=>{如果(response.err){this.$message("请求失败");}else{constresult=response.response.result;console.log("result:",result);this.result=result;}});},beforeDestroy(){this.subject.unsubscribe();},方法:{handleSearch(){这个.subject.next();},},};因为把数据当成了流,所以避免了时序问题:结语我整理了这么多,解决方案不止这些,还有像GraphQL等,了解的不多,没写到“种族”问题。“race”问题在一些简单的应用中出现的概率比较小,但是在一些复杂的应用中就比较容易出现。自从我从B端项目切换到活动页后,我就一直没遇到过这种问题(活动页高),但是我朋友遇到了这个问题,所以就简单整理了一下,有大概就这么多,谢谢阅读。
