龙空技术网

异常不仅仅是try/catch

php疑难杂症铺 795

前言:

目前我们对“非法uri是什么意思”大致比较讲究,同学们都需要知道一些“非法uri是什么意思”的相关文章。那么小编同时在网上网罗了一些有关“非法uri是什么意思””的相关知识,希望你们能喜欢,小伙伴们快快来学习一下吧!

异常不仅仅是try/catch前言

编程时我们往往拿到的是业务流程正确的业务说明文档或规范,但实际开发中却布满荆棘和例外情况,而这些例外中包含业务用例的例外,也包含技术上的例外。对于业务用例的例外我们别无它法,必须要求实施人员与用户共同提供合理的解决方案;而技术上的例外,则必须由我们码农们手刃之,而这也是我想记录的内容。

我打算分成《异常不仅仅是try/catch》和《调用栈,异常实例中的宝藏》两篇分别叙述内置/自定义异常类,捕获运行时异常/语法异常/网络请求异常/PromiseRejection事件,什么是调用栈和如何获取调用栈的相关信息。

是不是未出发就已经很期待呢?好吧,大家捉紧扶手,老司机要开车了^_^

一.异常还是错误?它会如何影响我们的代码?

在学习Java时我们会被告知异常(Exception)和错误(Error)是不一样的,异常是不会导致进程终止从而可以被修复(try/catch),但错误将会导致进程终止因此不能被修复。当对于JavaScript而言,我们要面对的仅仅有异常(虽然异常类名为Error或含Error字样),异常的出现不会导致JavaScript引擎崩溃,最多就是让当前执行的任务终止而已。

上面说到异常的出现最多就是让当前执行的任务终止,到底是什么意思呢?这里就涉及到Event Loop的原理了,下面我尝试用代码大致说明吧。

二.内置异常类型有哪些?

说到内置异常类那么必先提到的就是Error这个祖先类型了,其他所有的内置异常类和自定义类都必须继承它。而它的标准属性和方法就以下这寥寥几个而已

由于标准属性实在太少,无法提供更有效的信息供开发者定位异常发生的位置和重现事故现场,因此各浏览器厂家均手多多的自己增加些属性,然后逐渐成了事实标准。

另外巨硬还新增以下两个属性

那么现在我要实例化一个Error对象,只需调用Error()或new Error()即可;若想同时设置message,则改为Error("test")或new Error("test")。其实Error的构造函数签名是这样的

现在我们看看具体有哪些内置的异常类型吧!

1. EvalError,调用eval()时发生的异常,已被废弃只用于向后兼容而已

2. InternalError,JavaScript引擎内部异常,FireFox独门提供的!

3. RangeError,当函数实参越界时发生,如Array,Number.toExponential,Number.toFixed和Number.toPrecision时入参非法时。

4. ReferenceError,当引用未声明的变量时发生

5. SyntaxError,解析时发生语法错误

6. TypeError,当值不是所期待的类型时,null.f()也报这个错

7. URIError,当传递一个非法的URI给全局URI处理函数时发生,如decodeURIComponent('%'),即decodeURIComponent,decodeURIencodeURIComponent,encodeURI

三.动手写自己的异常类型吧!

关于在StackOverflow上早有人讨论如何自定义异常类型了参考

于是我们顺手拈来即可

cljs实现如下

四.捕获“同步代码”中的"运行时异常",用就够了

为了防止由于异常的出现,导致正常代码被略过的风险,我们习惯采取try/catch来捕获并处理异常。

cljs写法

很多时我们会以为这样书写就万事大吉了,但其实try/catch能且仅能捕获“同步代码”中的"运行时异常"。

1."同步代码"就是说无法获取如setTimeout、Promise等异步代码的异常,也就是说try/catch仅能捕获当前任务的异常,setTimeout等异步代码是在下一个EventLoop中执行。

2."运行时异常"是指非SyntaxError,也就是语法错误是无法捕获的,因为在解析JavaScript源码时就报错了,还怎么捕获呢~~

这时大家会急不可待地问:“异步代码的异常咋办呢?语法异常咋办呢?”在解答上述疑问前,我们先偏离一下,稍微挖挖throw语句的特性。

throw后面可以跟什么啊?

一般而言我们会throw一个Error或其子类的实例(如throw Error()),其实我们throw任何类型的数据(如throw 1,throw "test",throw true等)。但即使可以抛出任意类型的数据,我们还是要坚持抛出Error或其子类的实例。这是为什么

原因显然易见——异常发生时提供信息越全越好,更容易追踪定位重现问题嘛!

五."万能"异常捕获者window.onerror,真的万能吗?

在每个可能发生异常的地方都写上try/catch显然是不实际的(另外还存在性能问题),即使是罗嗦如Java我们开发时也就是不断声明throws,然后在顶层处理异常罢了。那么,JavaScript中对应的顶层异常处理入口又在哪呢?木有错,就是在window.onerror。看看方法签名吧

这时我们就可以通过它捕获除了try/catch能捕获的异常外,还可以捕获setTimeout等的异步代码异常,语法错误。

这样就满足了吗?还没出大杀技呢——屏蔽异常、屏蔽、屏~~

只有onerror函数返回true时,异常就不会继续向上抛(否则继续上抛就成了Uncaught Error了)。

现在回到标题的疑问中,有了onerror就可以捕获所有异常了吗?答案又是否定的(我的娘啊,还要折腾多久啊~0~)

1. Chrome中对于跨域脚本所报的异常,虽然onerror能够捕获,但统一报Script Error。若要得到正确的错误信息,则要配置跨域资源共享CORS才可以。

2. window.onerror实际上采用的事件冒泡的机制捕获异常,并且在冒泡(bubble)阶段时才触发,因此像网络请求异常这些不会冒泡的异常是无法捕获的。

3. Promise.reject产生的未被catch的异常,window.onerror也是无能为力。

六.Promise.reject也抛异常,怎么办?

通过Promise来处理复杂的异步流程控制让我们得心应手,但倘若其中出现异常或Promise实例状态变为rejected时,会是怎样一个状况,我们又可以如何处理呢?

Promise是如何标识异常发生的?

Promise实例的初始化状态是pending,而发生异常时则为rejected,而导致状态从pending转变为rejected的操作有

1. 调用Promise.reject类方法

2. 在工厂方法中调用reject方法

3. 在工厂方法或then回调函数中抛异常

当Promise实例从pending转变为rejected时,和之前谈论到异常一样,要么被捕获处理,要么继续抛出直到成为Uncaught(in promise) Error为止。

异常发生前就catch掉

若在异常发生前我们已经调用catch方法来捕获异常,那么则相安无事

专属于Promise的顶层异常处理

若在异常发生前我们没有调用catch方法来捕获异常,还是可以通过window的unhandledrejection事件捕获异常的

迟来的catch 

由于Promise实例可异步订阅其状态变化,也就是可以异步注册catch处理函数,这时其实已经抛出Uncaught(in promise) Error,但我们依然可以处理

另外,还可以通过window的rejectionhandled事件监听异步注册catch处理函数的行为

注意:只有抛出Uncaught(in promise) Error后,异步catch才会触发该事件。

七.404等网络请求异常真心要后之后觉吗?

也许我们都遇到<img src="./404.png">报404网络请求异常的情况,然后测试或用户保障怎么哪个哪个图标没有显示。其实我们我们可以通过以下方式捕获这类异常

由于网络请求异常不会冒泡,因此必须在capture阶段捕获才可以。但还有一个问题是这种方式无法精确判断异常的HTTP状态是404还是500等,因此还是要配合服务端日志来排查分析才可以。

标签: #非法uri是什么意思