Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
hawx1993 opened this issue May 22, 2017 · 1 comment
Open

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

hawx1993 opened this issue May 22, 2017 · 1 comment

Comments

@hawx1993
Copy link
Owner

hawx1993 commented May 22, 2017

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

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

Node.js错误处理

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

  • 操作异常应该被处理。程序员的失误不需要处理,如果处理了反而会影响错误排查
  • 操作异常有两种处理方式:同步 (try…catch) 和异步(callback, event - emitter)两种处理方式,但只能选择其中一种。
  • 缺少参数或参数无效是程序员的错误,一旦发生就应该 throw。

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

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

☞ 关于try...catch的使用

  • 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>标签添加 crossorigin 属性;
  • 在 js 所在的 cdn 服务器上添加 Access-Control-Allow-Origin: * HTTP 头;
  • 使用 throw new Error(“error message here”)
<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捕获

  • Promise不需要使用try...catch,直接使用.catch()
  • Generator 可以直接使用co 函数库来使用try...catch
  • async 和 await 可以直接使用try...catch

☞ crossOrigin参数跳过跨域限制

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

☞ 添加sourceMap文件

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

js原生错误类型

  • SyntaxError 解析代码时发生的语法错误。
  • ReferenceError 引用一个不存在的变量时发生的错误。
  • RangeError 当一个值超出有效范围时发生的错误。
  • TypeError是变量或参数不是预期类型时发生的错误。
@LiuMengzhou
Copy link

写的不错,就是有点乱

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants