用户
 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,登录网站

小程序社区 首页 教程 查看内容

捕获web与小程序JS异常的方法

Rolan 2019-11-26 00:50

前段时间,我们发现小程序异步代码里的错误没有上报到异常监控平台,经过排查这部分小程序没法直接监听到,需要我们手动上报。再者,开发过程中,异常处理一直是不太受重视且容易遗漏的模块。本文总结了JS异常的类型 ...

前段时间,我们发现小程序异步代码里的错误没有上报到异常监控平台,经过排查这部分小程序没法直接监听到,需要我们手动上报。再者,开发过程中,异常处理一直是不太受重视且容易遗漏的模块。本文总结了JS异常的类型及捕获方法,最后针对小程序给出错误处理经验。


JS异常分类

  • Error:常规异常,一般为用户自定义的异常,如 new Error("error message"),这类自定义的错误用来统计异常数据,直接上报即可;
  • RangeError:数值溢出越界异常,当 Number 类型变量超过指定的范围,如
    var pi = 3.14159; pi.toFixed(100000);d(toFixed()方法参数只能接受 0~100) ;
  • ReferenceError:引用异常,当不存在的变量被使用的时候,这类错误一般在编码检查阶段就会暴露;
  • SyntaxError:语法错误,作为解释型语言的 JavaScript 只有到执行的时候才能识别出语法错误,这类错误在编码检查和构建阶段就会暴露;
  • TypeError:类型错误,当调用不存在的对象方法或对象不存在时,例如:var foo = {}; foo.bar();为了避免这类错误出现,可使用防御式编程,但如若数据有误则应抛出异常;
  • URIError:encodeURI() decodeURI() 方法参数不正确抛出的异常,例如:decodeURIComponent("%");
  • EvalError:eval() 方法参数不正确抛出的异常;


Web端对异常的捕获能力

了解了异常的分类后,我们还需要知道浏览器如何捕获到这些异常。

1)try-catch

JavaScript 里有 try-catch 语法块,可用于异常捕获处理。try-catch 可以成功捕获大部分错误,但对于 SyntaxError 语法错误 和 异步代码中的错误,则无法捕获。

例如:SyntaxError 语法错误

try {
    var p = 
} catch(e) { 
    console.log('caught error: ', e.message)
}

// 输出:Uncaught SyntaxError: Unexpected token '}'复制代码

例如:异步代码中的错误

try {
    setTimeout(function() {
        var p = error + 1
    }, 0)
} catch(e) {
    console.log('caught error:', e.message)
}

// 输出:Uncaught ReferenceError: error is not defined复制代码

2)error 事件

try-catch 针对我们预感到可能会有问题的代码,捕获异常进行处理,而对于一些我们未知的错误,可以使用 window 对象的 error 事件进行监听。

error 事件可以捕获到同步或异步(非 Promise )代码中的非语法错误。

例如:异步代码中的错误

window.addEventListener('error', e => {
  console.log('caught error', e.message);
  e.preventDefault();
});
setTimeout(function() {
    var p = error + 1
}, 0)

// 输出:caught error Uncaught ReferenceError: error is not defined复制代码

需要注意的是:e.preventDefault() 在 error 监听事件中调用,可以阻止报告异常给浏览器,别让浏览器默认地在控制台输出错误。

另外,文档中也提到关于资源加载失败的错误:

When a resource (such as an <img> or <script>) fails to load, an error event using interface Event is fired at the element that initiated the load, and the onerror() handler on the element is invoked. These error events do not bubble up to window, but (at least in Firefox) can be handled with a window.addEventListener configured with useCapture set to True.

对于图片或脚本资源加载失败,这类错误不会冒泡给 window,但可以在捕获阶段进行处理,即addEventListener 最后一个参数置为 true

window.addEventListener('error', e => {
    console.log('资源加载失败');
}, true)复制代码

3)unhandledRejection 与 rejectionhandled 事件

error 事件可以捕获到非 Promise 的异步错误,而针对 Promise,window对象有专门的事件来处理这类错误。

当异步错误没被 catch 住时,触发 unhandledRejection 事件:

window.addEventListener('unhandledrejection', e => {
    console.log('caught unhandledrejection', e.reason);
    e.preventDefault();
})
var p = new Promise(function(resolve, reject) {
    tp = error + 1
})

// 输出:caught unhandledrejection ReferenceError: error is not defined复制代码

而当异步错误一开始未被 catch 住,过后才被 catch 的情况,会先触发 unhandledRejection 事件,当被 catch 的时候,会触发 rejectionhandled 事件:

window.addEventListener('unhandledrejection', e => {
    console.log('caught unhandledrejection', e.reason);
    e.preventDefault();
})
window.addEventListener('rejectionhandled', e => {
    console.log('caught rejectionhandled', e.reason);
    e.preventDefault();
})
var p = new Promise(function(resolve, reject) {
    tp = error + 1
})
setTimeout(() => {
    p.catch(e => console.log('catch', e.message))
}, 1000)

// 输出:
// caught unhandledrejection ReferenceError: error is not defined
// (1s后)
// catch error is not defined
// caught rejectionhandled ReferenceError: error is not defined复制代码

也就是说未处理的异常增加时会触发 unhandledRejection,而未处理的异常(被处理后)减少时会触发 rejectionhandled,这在上报异常中可以避免上报那些已经被处理过的异常。

关于兼容性,截止至本文成稿,移动端的支持程度还是可以的,iOS主流版本 和 Chrome 都支持:


(图来源:unhandledrejection/rejectionhandled events,caniuse.com/#search=unh…


小程序端对异常的捕获能力

小程序的 App 对象中有 onError 方法,相当于 web 端的 error 事件,可以捕获到同步或异步(非 Promise )代码中的非语法错误。而对于 Promise,小程序并没有如 window 对象中的 unhandledRejection 与 rejectionhandled 事件,无法像 web 端那样统一处理异常。

不过,既然都是 Promise 相关的错误,那么,我们可以改写或覆盖 Promise 对象,将其进行封装把所有错误都 catch 住也就可以了。

推荐 promise-polyfill 这个轻量级的 promise 实现包,其中提供了 _unhandledRejectionFn 方法,用于捕获那些未被处理的 Promise 异常。

import Promise from 'promise-polyfill';
Promise._unhandledRejectionFn = function(rejectError) {
    // 处理异常或上报
}
复制代码


总结

关于JS的异常总结已经差不多了,之前一直觉得这部分知识不够系统,一来自己重视程度不够,二来也是知识点不多但都较零散。经过这段时间收集资料,捋清思路,编码实现还算有所收获,便总结成文,若有不尽不祥不对之处烦请各位读者多多指点。


参考文献

  1. Exceptional Exception Handling in JavaScript
  2. 前端代码异常监控实战
  3. GlobalEventHandlers.onerror
  4. Promise rejection events in Using Promises
  5. promise-polyfill
鲜花
鲜花
鸡蛋
鸡蛋
分享至 : QQ空间
收藏
原作者: w_西城 来自: 掘金