hawx1993 / tech-blog

📦My personal tech blog,not regularly update
http://sf.gg/u/trigkit4/articles
339 stars 30 forks source link

前端异常捕获与错误处理 #13

Open hawx1993 opened 7 years ago

hawx1993 commented 7 years ago

前端异常监控主要是解决两大异常情况:

a. 页面中的javascript异常
b. 静态资源异常(使用addEventListener('error', handler, true)来捕获静态资源异常,包括js、img、css等;)

Node.js错误处理

Node.js 中推荐的异常处理方式:

由于try/catch无法捕捉异步回调里的异常,Node.js原生提供uncaughtException事件挂到process对象上,用于捕获所有未处理的异常:

process.on('uncaughtException', function(err) {
    console.error('Error caught in uncaughtException event:', err);
});

☞ 关于try...catch的使用

try {
    throw new Error('出错了!');
} catch (e) {
    console.log(e.name + ": " + e.message);//Error:  出错了!
    console.log(e.stack);//Error: 出错了  at (index):29
}

异步函数的异常捕获

因为异步函数的回调是在事件队列里单独拉出来执行的。所以在异步函数外面包裹try-catch是无法捕捉到回调函数里抛出的异常的。因为当回调函数从队列里被拉出来执行的时候try-catch所在的代码块已经执行完毕了。

try {
    setTimeout(() => {
        throw new Error('callback error'); 
    }, 0);
} catch (e) {
    console.error('caught callback error');
}
console.log('try-catch block ends');

在上述例子中,当回调里的异常被抛出但没被捕获的时候,该异常会直接被主程序所捕获。在浏览器里可以通过window.onerror,在node里通过process.uncaughtException可以捕获此类异常。

☞ 关于 window.onerror 的使用

window.onerror,是我们在做错误监控中用到比较多的方案。window.onerror包含了try...catch的优势,而try...catch无法捕获的语法错误和全局异常处理,window.onerror都可以做到。不过,由于是全局监测,就会统计到浏览器插件中的 js 异常。

window.onerror 算是一种特别暴力的容错手段,try..catch 也是如此,他们底层的实现就是利用 C/C++ 中的 goto 语句实现,一旦发现错误,不管目前的堆栈有多深,不管代码运行到了何处,直接跑到顶层或者 try..catch 捕获的那一层,这种一脚踢开错误的处理方式并不是很好。

当然,window.onerror 还有一个问题就是浏览器跨域,页面和 js 代码在不同域上时,浏览器出于安全性的考虑,会将异常内容隐藏,我们只能获取到一个简单的 Script Error信息。解决方案也很简单:

<script>
    window.onerror = function () {
        console.log(arguments)
        return true;
    }
    throw new Error('show error');
</script>

image

值得一提的是,页面中可能有好几个 script 标签, window.onerror 这个错误监听一定要放到最前头,否则将监听不到错误。如果将throw new Error放前头,throw new Error('show error');将直接报错,也就无法往下执行

异步(Promise)环境下错误处理方式

在 Promise 内部使用 reject 方法来处理错误,而不要直接调用 throw Error,这样你不会捕捉到任何的报错信息。

<script>

function errorFn() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('我可以被捕获')
            throw Error('永远无法被捕获');//该错误信息无法在catch中捕获
        })
    })
}

Promise.resolve(true).then((resolve, reject) => {
    return errorFn()
}).catch(error => {
    console.log('捕获异常', error) // 捕获异常 我可以被捕获
});
</script>

执行回调时已经不是处于原本的执行栈了,Error发生在下一轮事件循环中,所以没有被try...catch捕获

☞ crossOrigin参数跳过跨域限制

image 和 script 标签都有 crossorigin 参数,它的作用就是告诉浏览器,我要加载一个外域的资源,添加跨域支持

☞ 添加sourceMap文件

线上代码我们一般都会进行压缩处理,压缩代码无法定位到错误的具体位置,为了能快速定位错误,我们需要添加sourceMap文件

js原生错误类型

LiuMengzhou commented 7 years ago

写的不错,就是有点乱