js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报

相关博客:

js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报
js中的错误监控(二)【网络+资源加载】的错误捕获与上报

文章目录

一、错误类型

主要分成两类——代码运行错误资源请求错误

1.代码运行错误

运行代码时发生的错误有非常多。每种错误都有相应的错误类型。ECMA-262定义了7种错误类型:

1.Error 错误,通用的异常对象。

Error是基类型。其它类型继承自它。我们通常使用Error来自定义异常,Error对象有name和message属性,可以通过message来得到具体的错误信息,比如

let error = new Error('接口报错');
let name = error.name; // 'Error'
let msg = error.message;    // '接口报错'

2.EvalError 调用eval()函数抛出错误

3.RangeError 引用错误

超出指定范围错误,比如声明一个负数的数组,使用toFixec超过了规定小数的位数(0-20)

new Array(-1)
(1.2).toFixed(21)

4.ReferenceError 參数错误,访问未定义的变量

function foo() { 
    bar++;    // bar未定义
}

5.SyntaxError 语法错误,一般代码语句不完整

let a = 1 > 0 ?    // 正则不完整
if (a) {          // 少了一个分号

6.TypeError 类型错误,一个变量不是函数,却把它当做函数来调用

let a = 1;
a();    // 类型错误

7.URIError 编码错误,在使用encodeURI()和decodeURI()时。假设URI格式不对时,会导致URIError错误。

encodeURI('\uD800')
encodeURIComponent('\uD800')

2.资源加载错误

这个错误通常是找不到文件(404)或者是文件加载超时造成的。
详见:
js中的错误监控(二)【网络+资源加载】的错误捕获与上报

二、错误冒泡

错误也具有冒泡传播性
如果在一个函数内部发生了错误,它自身没有捕获,错误就会被抛到外层调用函数,如果外层函数也没有捕获,该错误会一直沿着函数调用链向上抛出,直到被JavaScript引擎捕获,代码终止执行。
所以,我们不必在每一个函数内部捕获错误,只需要在合适的地方来个统一捕获,一网打尽

三、捕获代码错误

比如语法错误、逻辑错误,针对不会被代码检查插件发现的错误

1. try…catch语句

我们可以通过try/catch语法来捕捉错误。最常用的是在函数里面捕捉错误,有错误就在catch处理。

try { 
  throw new Error("接口错误");
  console.log("try错误后面的语句不会被执行");
} catch (error) { 
  // 捕获错误
  console.log("try-catch捕获", error);
} finally { 
  //finally为了防止出现异常后。无法往下再运行的备用。
  console.log("finally 我都会运行!");
}
console.log("try-catch后面的语句");

控制台:
《js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报》

捕捉内置对象类型:

//try-catch
try 
  // 运行可能出错的代码
  a++;
  console.log("try错误后面的语句不会被执行");
} catch (error) { 
  // 捕获错误
  console.log("try-catch捕获", error);
}
console.log("try-catch后面的语句");

控制台:

《js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报》
只有一个message信息量较少,可以通过新建一个自动义错误类,可以拿到出错的信息,堆栈,出错的文件、行号、列号;
捕捉自定义error:

// Create a custom error
var SpecifiedError = function SpecifiedError(message) { 
this.name = 'SpecifiedError';
this.message = message || '';
this.stack = (new Error()).stack;
};
 
SpecifiedError.prototype = new Error();
SpecifiedError.prototype.constructor = SpecifiedError;

缺点:

  • 无法捕捉到SyntaxError语法错误,只能捕其他错误类型;如果尝试捕获语法错误,try-catch不会生效
try { 
  // 运行可能出错的代码
  let b = 1;
  let b = 2; //SyntaxError不可以被捕获
  console.log("try错误后面的语句不会被执行");
} catch (error) { 
  // 捕获错误
  console.log("try-catch捕获", error);
} finally { 
  //finally为了防止出现异常后。无法往下再运行的备用。
  console.log("finally 我都会运行!");
}
console.log("try-catch后面的语句");

控制台:
《js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报》

  • try catch只能捕获同步代码的异常,对回调,setTimeout,promise等无能为力
  • 对每一个函数都需要进行try/catch捕捉再进行处理,需要写很多重复的代码
  • 能够通过改动代码解决或浏览器兼容错误。不建议使用try-catch,由于它比一般语句消耗资源

2. window.onerror事件

使用一个全局的error事件来捕获所有的error,通过回调函数来处理错误,使用三个参数来调用:msg(错误消息)、url(发生错误的页面的 url)、line(发生错误的代码行)。

function message() { 
  adddlert("Welcome guest!");
}
window.onerror = function(message, source, lineno, colno, error) { 
  // 错误信息,源文件,行号
  console.log(message + "\n" + source + "\n" + lineno);
  // 禁止浏览器打印标准的错误信息
  return true;
};

控制台:
《js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报》

3.异步代码错误

一般情况下:异步错误只能在异步的函数内部捕获和处理,因为出现了异步操作时,不会立即执行,js引擎继续执行同步操作,捕获错误就是一个同步操作。也就是说在捕获错误的时候异步操作里的错误还没发生,所以需要在异步函数的内部去捕获。
ES6中为了处理异步,增加了promise、generator和async,它们各自都有不同的内部错误处理方式
try/catch无法捕捉异步函数整体抛出的错误:

try { 
    setTimeout(() => { 
        throw new Error('async error')
    }, 0)
} catch (e) { 
    console.log(e.message)
}

// async/await
async function foo () { 
    let a = 1;
    let b = await a + 2;
    console.log(b);
    throw new Error('async error')
}

try { 
    foo();
} catch (e) { 
    console.log(e.message);
}

控制台报错Uncaught Error: async error
《js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报》
正确的做法:

1.try/catch捕捉异步函数内部抛出的错误
2. .catch捕捉promise异步整体错误,遵循链式法则

3.1.setTimeout

try-catch捕获setTimeout回调函数内部的错误:

// 在异步代码块里面的同步代码就可以捕捉到
setTimeout(() => { 
  try { 
    throw new Error("async error");
  } catch (e) { 
    console.log("setTimeout回调里面的错误", e.message);
  }
}, 0); //setTimeout回调里面的错误 async error
3.2.promise
new Promise( function(resolve, reject) { ...} /* executor */  );

executor是带有 resolvereject 两个参数的函数 ,Promise构造函数执行时(在实例化之前)立即调用executor 函数,executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败), resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。
《js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报》resolve 和 reject 函数前后面的代码会同步执行,resolve 和 reject 函数作为回调异步执行,例子:

//resolve()以后的语句会被执行
var promise3 = new Promise((resolve, reject) => { 
  resolve("promise3-res");
  console.log("promise3-resolve以后");
});
promise3.then(res => { 
  console.log(res);
});
//promise3-resolve以后
//promise3-res

//reject()以后的语句会被执行,
// 直接reject(“错误信息”)不可以被.catch捕捉,会报错Uncaught (in promise)
var promise4 = new Promise((resolve, reject) => { 
  reject("promise4-res-catch");
  console.log("promise4-resolve以后");
});
promise3.catch(res => { 
  console.log(res);
});
// promise4-resolve以后
//promise.js:9 Uncaught (in promise) promise4-res-catch

注意:直接reject(“错误信息”)不可以被.catch捕捉,会报错Uncaught (in promise),接下来马上会讲到reject的正确使用方法。

1⃣️捕捉executor里的错误
如果在executor函数中throw一个错误,那么该promise 状态变为rejected。executor函数返回值会被忽略,throw之后的代码不会被执行,.catch无法捕捉错误

//throw new Error()使得该promise 状态为rejected。executor函数的返回值被忽略。
//throw之后的代码不会被执行
var promise6 = new Promise((resolve, reject) => { 
  throw new Error("promise6-res-catch");
  console.log("promise6-resolve以后");
});
promise5.catch(function(e) { 
  console.log(e);
});
// Uncaught (in promise) Error: promise6-res-catch

方法一:reject(new error())将error对象传给reject回调,.catch可以捕获错误

//reject(new error())可以被.catch捕捉错误,能获取返回值
var promise5 = new Promise((resolve, reject) => { 
  reject(new Error("promise5-res-catch"));
});
promise5.catch(function(e) { 
  console.log(e); //Error: promise5-res-catch
});

方法二: try-catch捕获promise同步函数内部的错误,然后通过 reject(e)传递给.catch方法

var promise = new Promise(function(resolve, reject) { 
  try { 
    throw new Error("Promise同步语句里面的错误");
  } catch (e) { 
    reject(e);
  }
});
promise
  .then(res => { 
    console.log("resolve");
  })
  .catch(e => { 
    console.log("promise-reject", e); //promise-reject Error: Promise同步语句里面的错误
  });

2⃣️捕捉.then .catch链式回调里面的错误
方法一.catch方法还可以处理链式调用.then中的错误,只处理第一个错误:

var promise2 = new Promise(function(resolve, reject) { 
  resolve();
});
promise2
  .then(function() { 
    // if some error throw
    throw new Error("Promise.then里面的错误1");
  })
  .then(function() { 
    // if some error throw
    throw new Error("Promise.then里面的错误2");
  })
  .catch(function(e) { 
    //something to deal with the error
    console.log(e); //Promise.then里面的错误1
  });

catch方面里面还可以再抛错误,这个错误会被后面的catch捕获。
3⃣️promise无法被捕捉的错误

  1. 如果组成Promise.all的promise有自己的错误捕获方法,那么Promise.all中的catch就不能捕获该错误。
  2. 在promise实例resolve之后,错误无法被捕获。
var p1=new Promise(function(resolve,reject){ 
  reject(new Error('test1'))
}).catch(function(e){ 
  console.log("由p1自身捕获",e);
})
var p2=new Promise(function(resolve,reject){ 
  resolve();
})
var p=Promise.all([p1,p2]);
p.then(function(){ 

}).catch(function(e){ 
  //在此处捕获不到p1中的error
  console.log(e)
})
//由p1自身捕获 Error: test1
3.3.async-await

trycatch直接捕获async-await同步语句的错误:

async function foo() { 
  try { 
    let a = 1;
    let b = (await a) + 2;
    console.log("async-await,b", b);
    throw new Error("async同步语句里面的错误");
  } catch (e) { 
    console.log("async-await", e.message); //async同步语句里面的错误 async error
  }
}
foo();

此外,async里面throw Error 相当于返回Promise.reject。所以也可以直接在内部/外部使用.catch捕捉错误

//async.catch
async function F1() { 
  throw new Error("f1-async里的错误,相当于执行reject(e)");
}
var f1 = F1();
f1.catch(function(e) { 
  console.log(e); //Error: f1-async里的错误,相当于执行reject(e)
});
async function F2() { 
  await Promise.reject("f2-async里的错误,相当于执行reject(e)").catch(function(
    e
  ) { 
    console.log(e);
  });
}
var f2 = F2(); // f2-async里的错误,相当于执行reject(e)

3.4.gennerator

try-catch捕获gennerator.throw的错误
Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。
如果Generator的错误没有被捕获,就不会继续执行,只要错误没有被处理,就会返回done:true就停止执行generator

// gennerator
function* F() { 
  try { 
    yield 1;
  } catch (e) { 
    console.log("gennerator-throw", e);
  }
  yield 2;
  return "gennerator-value";
}
var f = F();
f.next(); //{value :1,done:false}
//在函数体外抛出错误,然后在 Generator 函数体内捕获。
f.throw(new Error("gennerator-throw在函数体外抛出的错误")); //{value:2,done:false}
f.next(); //{value:3,done:true}

详见 http://es6.ruanyifeng.com/#docs/generator#Generator-prototype-throw

内部错误的控制台结果:先执行同步操作,所以f.throw先执行,被捕获,然后async函数的异步操作,所以3先被打印,然后执行async的await回调(相当于promise微事件),async的catch再被打印,再执行promise-reject,最后执行setTimeout的回调,所以最后打印setTimeout的catch
《js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报》

参考:https://blog.csdn.net/liwusen/article/details/79617903
https://www.cnblogs.com/fundebug/p/7989066.html

    原文作者:juwuyu
    原文地址: https://blog.csdn.net/weixin_28900307/article/details/89441295
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞