JavaScript捕获错误的方式

107 阅读2分钟

JavaScript捕获错误的方式

一、try-catch

try catch是我们常见的一种异常捕获方式

缺点1、无法捕获异步异常

比如promisesetTimeout

try {
    new Promise((resolve, reject) => {
      reject('err')
    })
  } catch (error) {
    console.log('catch error:', error)
  }
​
  try {
    setTimeout(() => {
      console.log(a)
    }, 100)
  } catch (error) {
    console.log('catch error:', error)
  }

image.png

缺点2、无法捕获语法异常

 try {
    console.log('答案cp3
  } catch (error) {
    console.log('catch error:', error)
  }

image.png

常见内置的标准错误类型

  • EvalError:错误与eval()有关
  • RangeError: 数值变量或者参数超出有效范围
  • RefenerceError:无效引用
  • SyntaxError:eval()在解析代码的过程中出现语法错误
  • TypeError:变量或者参数不属于有效类型
  • URLError:给encodeUrl()或者decodeUrl()传递的参数无效

二、监听error事件

有两种方式:

1.window.onerror

 window.onerror = function (message, source, line, col, error) {
    console.log("catch error:",{message, source, line, col, error});
    // message: 错误的信息
    // source:  错误发生的文件
    // line:    错误发生的行数
    // col:     错误发生的列数
    // error:   错误对象
  }; 

可以拿到错误的信息,堆栈,行数,列数等信息。

处于安全考虑,如果加载不同域的脚本出现错误时,语法错误的细节不会展示。

2.window.addEventListener('error', callback, useCapture)

 window.addEventListener('error', (error) => {
    console.log('catch error:', error)
  })

两者区别

这两者很相似,语法错误、代码错误都能捕获,和try-catch一样不能捕获异步错误

window.onerror = function (message, source, line, col, error) {
  console.log("catch error:",{message, source, line, col, error});
};
window.addEventListener('error', (error) => {
  console.log('error:', error)
})
new Promise((resolve, reject) => {
  reject('err')
}) 

image.png

  • window.onerror打印日志的方式比addEventListener的方式多一点。

  • 如果是资源加载失败,window.onerror无法捕获。addEventListener的方式可以捕获,但是得是在捕获阶段才行。

    • addEventListener的第三个参数用来指定事件的处理阶段
    • 事件的处理阶段分为捕获阶段和冒泡阶段,默认为捕获阶段:false
    • 捕获阶段就是addEventListener的第三个参数为true。
    • window.addEventListener('error', (error) => {
          console.log('catch error:', error)
      }, true)
        
      <img src="https://www.baidu.com/404.jpg" alt=""> 
      

三.监听unhandledrejection事件

对于上面没有捕获到的异步错误,使用监听unhandledrejection事件来捕获。

1、window.onunhandledrejection

window.onunhandledrejection = event => {
  console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`);
};

2、window.addEventListener("unhandledrejection",callback)

window.addEventListener('unhandledrejection', (error) => {
  console.log('catch error:', error)
})
new Promise((resolve, reject) => {
  reject('promise error')
}) 

image.png

注意事项

unhandledrejection只能捕获未显示处理的promise异常

// 能触发 unhandledrejection ,因为未显式处理
Promise.reject('test').then()

// 能触发 unhandledrejection ,因为未显式处理
Promise.reject('test').then(console.log)

// 不能触发 unhandledrejection ,因为已处理
Promise.reject('test').then(console.log, console.log)

// 不能触发 unhandledrejection ,因为没处理,直接抛出异常
Promise.reject('test')

五.Vue的errorHandler函数

Vue的异常捕获使用errorHandler实现,内部也是通过try-catch来捕获

Vue.config.errorHandler = (err, vm, info) => {
  console.log(err, vm, info)
  // err: 错误对象
  // vm: 发生错误的实例
  // info: 错误信息
}
throw new Error('i am err') 

image.png

统一异常处理

代码中抛出的异常,一种要展示给用户,一种要反馈给开发者。

对于展示给用户的错误,一般采用alert或者toast展示;对于展示给开发者的用户一般输出到控制台,或者上报给指定服务器。

在一个函数或者一个代码块中,把抛出的异常统一捕捉起来。按照不同的类型以不同的方式展示。

对于:

需要点击确认的异常类型

// ensureError.js
function EnsureError(message = 'default message'){
	this.name = 'EnsureError';
  this.message = message;
  this.stack = (new Error()).stack;
}
EnsureError.prototype = Object.create(Error.prototype);
EnsureError.prototype.construecor = EnsureError;

export default EnsureError;

弹窗提示的错误类型

// toastError.js
function ToastError(message = 'default message'){
	this.name = 'ToastError';
  this.message = message;
  this.stack = (new Error()).stack;
}
ToastError.prototype = Object.create(Error.prototype);
ToastError.prototype.constructor = ToastError;

export default ToastError;

提示开发者的异常类型

// devError.js
function DevError(message = 'default message'){
	this.name = 'DevError';
  this.message = message;
  this.stack = (new Error()).stack;
}
DevError.prototype = Object.create(Error.prototype);
DevError.prototype.constructor = DevError;

export default DevError;

异常处理器

抛出普通异常时,可以带上stackoverflow上问题的列表,方便开发者查找原因。

import EnsureError from './ensureError.js';
import ToastError from './toastError.js';
import DevError from './devError.js';
import EnsurePopup from './ensurePopup.js';
import ToastPopup from './toastPopup.js';

function errorHandler(err) {
    if (err instanceof EnsureError) {
        EnsurePopup(err.message);
    } else if (err instanceof ToastError) {
        ToastPopup(err.message);
    }else if( err instanceof DevError){
        DevError(err.message);
    }else{
        error.message += ` https://stackoverflow.com/questions?q=${encodeURI(error.message)}`
        console.error(err.message);    
    }
}

window.onerror = (msg, url, line, col, err) => {
    errorHandler(err);
}

window.onunhandledrejection = event =>{
    errorHandler(event.reason);
};

export default errorHandler;