一、为什么要处理异常
以前没有接触前端的时候我会想,平时我们浏览的一些网页也好,app也好,为什么很少将一些bug啊、各种异常啊之类的东西呈现在我们用户的眼前,难道是程序运行的过程中都没有出现异常的情况吗?那肯定不是的,那现在问题又来了,如果是出现了异常,为什么我们的用户在使用的过程中却几乎没有察觉到?我觉得也许这就是一款好的产品所必需的要点之一吧。试想,如果是因为各种突发的的异常情况就终止了整个程序的运行,那对用户来说真的是体验太差了!所以,我们前端工作人员就很有必要去阻止这种事情的发生,那具体该如何去做呢?这就需要我们前端在发生异常的时候就去捕获该异常并去处理它,不要将一些不必要的异常呈现给用户。好的,接下来我们就简单来讨论一下关于前端处理异常的一些问题。
处理异常的n+个理由:
增强用户体验;
远程定位问题;
未雨绸缪,及早发现问题并去处理它;
无法复线问题,尤其是移动端,机型,系统都是问题;
完善的前端方案,前端监控系统;
对于 JS 而言,我们面对的仅仅只是异常,异常的出现不会直接导致 JS 引擎崩溃,最多只会使当前执行的任务终止。
二、前端需要做哪些异常捕获
对于前端来说,我们可做的异常捕获还真的不少。总结一下,大概分为:
JS语法错误、代码异常
AJAX异常
静态资源加载异常
Promise异常
Iframe异常
跨域Script error
崩溃和卡顿
对于异常处理,try-catch和Promise catch经常会被我们经常用到,但是try-catch也会有一些我之前没有了解到的误区,Promise catch的功能也远比我之前了解的强大的多。
简单的分析一下try-catch的误区和Promise catch的相关知识
三、try-catch使用过程中的误区
1.try-catch只能捕获到同步的运行错误,对语法和异步错误去无能为力,捕获不到。
(1)可以捕获到同步的错误
try {
let name = 'huangdonglu';
console.log(nam);//在这里故意把name写成了nam
} catch(e) {
console.log('捕获到异常:',e);
}
输出:
捕获到异常: ReferenceError: nam is not defined at <anonymous>:3:15
可见,同步的错误可以被正常捕获到。
(2)不能捕获到语法的错误
try {
let name = 'jartto;//在这里故意将右边的单引号去掉
console.log(nam);
} catch(e) {
console.log('捕获到异常:',e);
}
输出:
Uncaught SyntaxError: Invalid or unexpected token
可见,语法的错误不能被捕获
**(3)不能捕获到异步的错误**
try {
setTimeout(() => {
undefined.map(v => v);//在这里设置成了undefined
}, 1000)
} catch(e) {
console.log('捕获到异常:',e);
}
输出:
Uncaught TypeError: Cannot read property 'map' of undefined at setTimeout (<anonymous>:3:11)
可见,异步错误也不能被捕获
四、Promise Catch
Promise 链在错误处理中十分强大,当一个promise被reject时, 控制权移交至最近的reject处理程序(handler) 。这在实际开发中非常方便。
promise catch的一个好处就是它可以 链式使用 ,可以不用像try-catch语句那样多重的嵌套,只要在前面的链式过程中有任何一个地方出错,都会被catch捕获到。
1.一般来说, 我们都会将.catch附加到链的末尾 ,在这种情况下,只要上述的任何一个Promise被reject,catch就会捕获它。
例如,下面这段代码:
new Promise((resolve, reject) => {
resolve("ok");
}).then((result) => {
throw new Error("IU"); // reject 这个 promise
}).catch(alert); // Error: IU!
2.Promise 的执行者(executor)和promise的处理程序(handler)周围有一个“ 隐式的"try-catch "。如果发生异常,它就会被捕获,并被视为"rejection"进行处理。
例如:下面这段代码:
new Promise((resolve, reject) => {
throw new Error("IU!");
}).catch(alert); // Error: IU!
与下面这段代码工作上完全相同:
new Promise((resolve, reject) => {
reject(new Error("IU!"));
}).catch(alert); // Error: IU!
在executor周围的“隐式try-catch”自动捕获了error,并将其变为rejected promise。
3.这不仅仅发生在executor函数中,同样也发生在handler中。 如果我们在.then处理程序(handler)中throw,这意味着promise被rejected,因此控制权移交至最近的error处理程序(handler) 。
例如,下面这一段代码:
new Promise((resolve, reject) => {
resolve("ok");
}).then((result) => {
throw new Error("IU!"); // reject 这个 promise
}).catch(alert); // Error: IU!
对于所有的error都会发生这种情况,而不仅仅是由throw语句导致的这些error。
例如,下面这一段代码:
new Promise((resolve, reject) => {
resolve("ok");
}).then((result) => {
blabla(); // 没有这个函数
}).catch(alert); // ReferenceError: blabla is not defined
最后的.catch不仅可以捕获显示的rejection,还会捕获它上面的处理程序(handler)中意外出现的error。
4.在常规的 try..catch 中,我们可以分析错误(error), 如果我们无法处理它,可以将其再次抛出 。对于 promise 来说,这也是可以的。
下面的这个例子中,catch可以正常处理error,所以下一个成功的.then处理程序(handler)就会被调用
// 执行流:catch -> then
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(function(error) {
alert("The error is handled, continue normally");
}).then(() => alert("Next successful handler runs"));
```
catch可以正常处理异常,.then handler 也可以正常运行。
下面这个例子中,catch捕获了异常,但是无法处理它,所以就会将其再次抛出
```
// 执行流:catch -> catch -> then
new Promise((resolve, reject) => {
throw new Error("IU!");
}).catch(function(error) { // (*)
if (error instanceof URIError) {
// 处理它
} else {
alert("Can't handle such error");
throw error; // 再次抛出此 error 或另外一个 error,执行将跳转至下一个 catch
}
}).then(function() {
/* 不在这里运行 */
}).catch(error => { // (**)
alert(`The unknown error has occurred: ${error}`);
// 不会返回任何内容 => 执行正常进行
});
```
执行从第一个catch沿着链跳转到下一个catch
####五、总结
在这里先简单介绍一下我们平时比较常用的两种前端异常处理的手段,但是前端处理异常的方法还有很多,还需要我们在不断的开发过程中去不断的学习。
如果你现在也想学习前端开发技术,在学习前端的过程当中有遇见任何关于学习方法,学习路线,学习效率等方面的问题,你都可以加入到我的Q群中:前114中6649后671,里面有许多前端学习资料以及2020大厂面试真题 点赞、评论、转发 即可免费获取,希望能够对你们有所帮助。
网友评论