当前位置: 首页 > Web前端 > JavaScript

从await-to-js到try-run-js

时间:2023-03-26 23:27:55 JavaScript

,我在做codereview的时候发现有同事用trycatch包裹了一堆异步代码,所以个人觉得很奇怪。catchonly不应该有问题吗?代码?有同事告诉我,如果trycatch太细了,会出现内外作用域不一致的情况,需要提前声明变量。letres:Data[]=[];try{res=awaitfetchData();}catch(err){//错误操作或终止//return}//继续执行正常逻辑的确,一方面,开发人员Scope不应该包装非异常代码。另一方面,提前声明变量会使代码不连贯,打断思路。一种方法是直接使用原生Promies而不是异步。fetchData().then((res)=>{}).catch((err)=>{});当然,单个异步请求是没有问题的,如果是有依赖的异步请求。虽然Promise中可以返回另一个Promise请求,但是这种方式只能有一个catch。fetchData().then((res)=>{//业务处理returnfetchData2(res);}).then((res)=>{//业务处理}).catch((err)=>{//我们只能做一般的错误处理});如果我们需要多次catch处理,就需要这样写。fetchData().then((res)=>{//业务处理returnfetchData2(res);}).catch((err)=>{//错误处理并返回nullreturnnull;}).then((res)=>{if(res===null){return;}//业务处理}).catch((err)=>{//错误处理});这时候开发者还要考虑fetchData2是否会返回null的问题。所以我个人开始寻找一些方法来帮助我们解决这个问题。await-to-jsawait-to-js是一个帮助开发者处理异步错误的库。我们先来看看这个库是如何解决我们的问题的。importtofrom"await-to-js";const[fetch1Err,fetch1Result]=awaitto(fetchData());if(fetch1Err){//错误操作或终止//return}const[fetch2Err,fetch1Result]=awaitto(fetchData2(fetch1Result));if(fetch2Err){//错误操作或终止//return}源码很简单。导出函数到(promise,errorExt,){returnpromise.then((data)=>[null,data]).catch((err)=>{if(errorExt){constparsedError=Object.assign({},err,errorExt);return[parsedError,undefined];}return[err,undefined];});}使用try-run-js看到await-to-js使用errors是正常流程的一部分,所以我个人想想trycatch能不能解决一些异步代码问题?我立即想到需要获取DOM节点。现有的框架都采用了数据驱动的思想,但是DOM什么时候渲染出来是未知的,所以个人认为之前的代码,Vue需要获取ref并进行回调处理。functionresolveRef(refName,callback,ti??me:number=1){//超过10次递归中断if(time>10)thrownewError(`cannotfindref:${refName}`);//constself=this;//获取ref节点constref=this.$refs[refName];如果(参考){回调(参考);}else{//下一次如果没有节点this.$nextTick(()=>{resolveRef.call(self,refName,callback,ti??me+1);});}}当然上面的代码确实可以解决这类问题,我们在处理这类问题的时候可以替换ref和nextTick的代码。于是在await-to-js的逻辑下,我自己开发了try-run-js这个库。让我们看看这个库是如何使用的。importtryRunfrom"try-run-js";tryRun(()=>{//尽量直接使用正常逻辑代码//不要加?。//代码不会出错,不会重试this.$refs.navTree.setCurrentKey("xxx");},{//重试次数retryTime:10,//下一次操作超时前的延迟时间:()=>{newPromise((resolve)=>{this.$nextTick(解决);});},});我们还可以获得错误数据和结果。从“try-run-js”导入tryRun;constgetDomStyle=async()=>{//一步获取const{error:domErr,result:domStyle}=awaittryRun(()=>{//返回dom节点样式,不管是否有ppt//不加吗。//代码不会出错返回undefinedreturndocument.getElementById("domId").style;},{//重试次数retryTime:3,//ReturnFornumbers,functionwillusesetTimeout//参数为当前重试次数,第一次重试100ms,第二次200timeout:(time)=>time*100,//也可以不传直接返回次数默认是333//超时:333});如果(domErr){返回{};}返回domStyle;};当然,这个库也支持返回元组和await-to-jsPromise错误处理的功能。从“try-run-js”导入{tryRunForTuple};const[error,result]=awaittryRunForTuple(fetchData());try-run-js项目演进try-run-js的核心在于try-catch的处理,下面是关于try-run-js的编写思路。希望能对大家有所帮助。支持await-to-jsconstisObject=(val:any):valisObject=>val!==null&&(typeofval==="object"||typeofval==="function");constisPromise=(val:any):valisPromise=>{//继承Promise//有then和catch函数,对应手写的PromisereturnvalinstanceofPromise||(isObject(val)&&typeofval.then===“函数”&&typeofval.catch===“函数”);};consttryRun=async(//functionorpromisepromiseOrFun:Promise|Function,//配置项options?:TryRunOptions,):Promise>=>{//当前参数是否为承诺construnParamIsPromise=isPromise(promiseOrFun);construnParamIsFun=typeofpromiseOrFun===“函数”;//函数和Promise都不会直接返回错误返回{错误:paramsError}作为TryRunResultRecord;}if(runParamIsPromise){//直接使用await-to-js代码returnrunPromise(promiseOrFunasPromise);}};执行错误重试接下来我们开始使用trycatch来捕获函数的错误并重试//默认超时constDEFAULT_TIMEOUT:number=333//异步等待constsleep=(timeOut:number)=>{returnnewPromise(resolve=>{setTimeout(()=>{resolve()},timeOut)})}consttryRun=async(promiseOrFun:Promise|Function,options?:TryRunOptions,):Promise>=>{const{retryTime=0,timeout=DEFAULT_TIMEOUT}={...DEFAULT_OPTIONS,...选项,};//当前重试次数letcurrentTime:number=0;//成功与否letisSuccess:boolean=false;让结果;让错误:错误;while(currentTime<=retryTime&&!isSuccess){try{result=awaitpromiseOrFun();//执行完成并得到结果后,即认为成功isSuccess=true;}catch(err){error=err作为错误;//尝试次数加一currentTime++;//这里注意一下,作者这里犯了一些错误//如果没有处理好,会执行不需要处理的await//1.如果不需要重新请求(重试次数为0),直接跳过//2.上次也失败(重试结束)if(retryTime>0&¤tTime<=retryTime){//获取时间letfinalTimeout:number|Promise=typeoftimeout==="number"?超时:默认超时;//如果是函数就执行函数if(typeoftimeout==="function"){finalTimeout=timeout(currentTime);}//目前返回Promise,直接等待if(isPromise(finalTimeout)){awaitfinalTimeout;}else{//如果最终结果不是数字,则改为默认数据if(typeoffinalTimeout!=="number"){finalTimeout=DEFAULT_TIMEOUT;}//这里我尝试使用NaN,-Infinity,Infinity//发现已经处理了setTimeout,下面是浏览器的处理方式//如果timeout是一个Infinity值,一个Not-a-Number(NaN)值,或负数,让超时为零。//负数、无穷大和NaN都会变成0awaitsleep(finalTimeout);}}}}//如果成功或失败则返回if(isSuccess){return{result,error:null};}返回{错误:错误!,结果:未定义};};这样,我们就基本完成了try-run-js。添加tryRunForTuple函数很简单,直接tryRun并转换结果:consttryRunForTuple=(promiseOrFun:Promise|Function,options?:TryRunOptions):Promise>=>{returntryRun(promiseOrFun,options).then(res=>{const{result,error}=resif(error){返回[error,undefined]as[any,undefined]}return[null,result]as[null,T]})}代码在try-run-js里,什么情况下还用try-run-js呢?同时也欢迎大家提issue和pr给我鼓励。如果您觉得这篇文章不错,希望您能给我一些鼓励,并帮我在我的github博客下star。博客地址参考await-to-jstry-run-js