js中的错误监控(一)【promise,async,generator异步+内置错误类型】的错误捕获与上报
js中的错误监控(二)【网络+资源加载】的错误捕获与上报
主要分成两类——代码运行错误
和资源请求错误
运行代码时发生的错误有非常多。每种错误都有相应的错误类型。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')
这个错误通常是找不到文件(404)或者是文件加载超时造成的。
详见:
js中的错误监控(二)【网络+资源加载】的错误捕获与上报
错误也具有冒泡传播性
如果在一个函数内部发生了错误,它自身没有捕获,错误就会被抛到外层调用函数,如果外层函数也没有捕获,该错误会一直沿着函数调用链
向上抛出,直到被JavaScript引擎捕获
,代码终止执行。
所以,我们不必在每一个函数内部捕获错误,只需要在合适的地方来个统一捕获,一网打尽
比如语法错误、逻辑错误,针对不会被代码检查插件发现的错误
我们可以通过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后面的语句");
捕捉内置对象类型:
//try-catch
try
// 运行可能出错的代码
a++;
console.log("try错误后面的语句不会被执行");
} catch (error) {
// 捕获错误
console.log("try-catch捕获", error);
}
console.log("try-catch后面的语句");
控制台:
只有一个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后面的语句");
同步代码的异常
,对回调,setTimeout,promise等无能为力重复的代码
消耗资源
使用一个全局的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引擎继续执行同步操作,捕获错误就是一个同步操作。也就是说在捕获错误的时候,异步操作里的错误还没发生
,所以需要在异步函数的内部去捕获。
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
:
正确的做法:
1.
try/catch
捕捉异步函数内部
抛出的错误
2..catch
捕捉promise异步整体错误
,遵循链式法则
try-catch捕获setTimeout回调函数
内部的错误:
// 在异步代码块里面的同步代码就可以捕捉到
setTimeout(() => {
try {
throw new Error("async error");
} catch (e) {
console.log("setTimeout回调里面的错误", e.message);
}
}, 0); //setTimeout回调里面的错误 async error
new Promise( function(resolve, reject) {...} /* executor */ );
executor
是带有 resolve
和 reject
两个参数的函数 ,Promise构造函数执行时(在实例化之前)立即调用
executor 函数,executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败), resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。
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无法被捕捉的错误
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
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)
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
参考:https://blog.csdn.net/liwusen/article/details/79617903
https://www.cnblogs.com/fundebug/p/7989066.html