fix: error handle.
This commit is contained in:
parent
0f8797e8ee
commit
5aad83b007
|
@ -1,36 +1,49 @@
|
||||||
/**
|
/**
|
||||||
* 关于`Google Chrome`的异步错误处理
|
* 关于`Google Chrome`的异步错误处理
|
||||||
*
|
*
|
||||||
* `Chrome`所用的`v8`引擎为`Error`提供了特有的报错栈堆处理函数,用于用户自定义报错栈堆的内容。
|
* 由于`v8`提供的`Error.prepareStackTrace(error, structuredStackTrace)`接口存在一些限制,导致不适合无名杀
|
||||||
*
|
*
|
||||||
* 我们用到了`Error.prepareStackTrace(error, structuredStackTrace)`这个函数,这个函数的信息可参考[这里](https://v8.dev/docs/stack-trace-api#customizing-stack-traces)
|
* 故我们直接解析`Error`的栈堆信息,来获取相关内容
|
||||||
*
|
*
|
||||||
* 该函数提供了结构化的栈堆信息,很幸运的是,这个结构化的栈堆能直接告诉我们报错的文件以及位置,故我们使用该函数,让异步报错能直接定位原始位置
|
* ~~`Chrome`所用的`v8`引擎为`Error`提供了特有的报错栈堆处理接口,用于用户自定义报错栈堆的内容。~~
|
||||||
|
*
|
||||||
|
* ~~我们用到了`Error.prepareStackTrace(error, structuredStackTrace)`这个接口,这个接口的信息可参考[这里](https://v8.dev/docs/stack-trace-api#customizing-stack-traces)~~
|
||||||
|
*
|
||||||
|
* ~~该接口提供了结构化的栈堆信息,很幸运的是,这个结构化的栈堆能直接告诉我们报错的文件以及位置,故我们使用该接口,让异步报错能直接定位原始位置~~
|
||||||
*
|
*
|
||||||
* @implements {PromiseErrorHandler}
|
* @implements {PromiseErrorHandler}
|
||||||
*/
|
*/
|
||||||
export class ChromePromiseErrorHandler {
|
export class ChromePromiseErrorHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于临时记录报错信息的列表,通过`Error.prepareStackTrace`更新该列表
|
* ~~用于临时记录报错信息的列表,通过`Error.prepareStackTrace`更新该列表~~
|
||||||
*
|
*
|
||||||
* @type {[Error, NodeJS.CallSite[]][]}
|
* 现在用于存储报错过的错误信息
|
||||||
|
*
|
||||||
|
* @type {Error[]}
|
||||||
*/
|
*/
|
||||||
#errorList;
|
#errorList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {typeof Error.prepareStackTrace}
|
* @type {typeof Error.prepareStackTrace}
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
#originErrorPrepareStackTrace;
|
#_originErrorPrepareStackTrace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化`Error.prepareStackTrace`,将该值赋值成我们需要的函数
|
* 判断是否是v8错误栈堆用到的正则
|
||||||
|
*/
|
||||||
|
#STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ~~初始化`Error.prepareStackTrace`,将该值赋值成我们需要的函数~~
|
||||||
*
|
*
|
||||||
* 未防止本来Error.prepareStackTrace便存在赋值的行为,我们将原始值存储,并在需要的函数中调用
|
* ~~未防止本来Error.prepareStackTrace便存在赋值的行为,我们将原始值存储,并在需要的函数中调用~~
|
||||||
*
|
*
|
||||||
* > 这或许就是本体扩展化的第一步(小声)
|
* 初始化存储报错信息的列表
|
||||||
*/
|
*/
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
/*
|
||||||
this.#errorList = [];
|
this.#errorList = [];
|
||||||
this.#originErrorPrepareStackTrace = Error.prepareStackTrace;
|
this.#originErrorPrepareStackTrace = Error.prepareStackTrace;
|
||||||
Error.prepareStackTrace = (error, stackTraces) => {
|
Error.prepareStackTrace = (error, stackTraces) => {
|
||||||
|
@ -42,13 +55,19 @@ export class ChromePromiseErrorHandler {
|
||||||
this.#errorList.push([error, stackTraces]);
|
this.#errorList.push([error, stackTraces]);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
this.#errorList = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将原来可能的`Error.prepareStackTrace`赋值回去
|
* ~~将原来可能的`Error.prepareStackTrace`赋值回去~~
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
onUnload() {
|
onUnload() {
|
||||||
|
/*
|
||||||
Error.prepareStackTrace = this.#originErrorPrepareStackTrace;
|
Error.prepareStackTrace = this.#originErrorPrepareStackTrace;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,6 +79,61 @@ export class ChromePromiseErrorHandler {
|
||||||
*/
|
*/
|
||||||
onHandle(event) {
|
onHandle(event) {
|
||||||
event.promise.catch((error) => {
|
event.promise.catch((error) => {
|
||||||
|
// 如果`error`是个错误,则继续处理
|
||||||
|
if (error instanceof Error) {
|
||||||
|
// 如果已经处理过该错误,则不再处理
|
||||||
|
if (this.#errorList.includes(error)) return;
|
||||||
|
this.#errorList.push(error);
|
||||||
|
// 如果`error`拥有字符串形式的报错栈堆,且报错栈堆确实符合v8的stack
|
||||||
|
if (typeof error.stack === 'string' && this.#STACK_REGEXP.test(error.stack)) {
|
||||||
|
// 获取符合栈堆信息的字符串,一般来说就是从第二行开始的所有行
|
||||||
|
// 为了处理eval的情况,故必须获取完行数
|
||||||
|
let lines = error.stack.split('\n').filter((line) =>
|
||||||
|
this.#STACK_REGEXP.test(line));
|
||||||
|
|
||||||
|
// 提供类型信息防止vscode报错
|
||||||
|
/**
|
||||||
|
* @type {string | undefined}
|
||||||
|
*/
|
||||||
|
let fileName = void 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {string | undefined}
|
||||||
|
*/
|
||||||
|
let line = void 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {string | undefined}
|
||||||
|
*/
|
||||||
|
let column = void 0;
|
||||||
|
|
||||||
|
// 从第一条开始遍历,一直遍历到不存在eval的位置
|
||||||
|
for (let currentLine = 0; currentLine < lines.length; ++currentLine) {
|
||||||
|
if (/\(eval /.test(lines[currentLine])) continue;
|
||||||
|
|
||||||
|
let formatedLine = lines[currentLine].replace(/^\s+/, '').replace(/\(eval code/g, '(').replace(/^.*?\s+/, '');
|
||||||
|
|
||||||
|
const location = formatedLine.match(/ (\(.+\)$)/);
|
||||||
|
if (location) formatedLine = formatedLine.replace(location[0], '');
|
||||||
|
|
||||||
|
const locationParts = extractLocation(location ? location[1] : formatedLine);
|
||||||
|
|
||||||
|
fileName = ['eval', '<anonymous>'].includes(locationParts[0]) ? void 0 : locationParts[0];
|
||||||
|
line = locationParts[1];
|
||||||
|
column = locationParts[2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
window.onerror(error.message, fileName, line, column, error);
|
||||||
|
}
|
||||||
|
// 反之我们只能不考虑报错文件信息,直接调用onerror
|
||||||
|
else {
|
||||||
|
// @ts-ignore
|
||||||
|
window.onerror(error.message, void 0, void 0, void 0, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
console.error(error)
|
console.error(error)
|
||||||
const result = this.#errorList.find(savedError => savedError[0] === error);
|
const result = this.#errorList.find(savedError => savedError[0] === error);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -72,17 +146,42 @@ export class ChromePromiseErrorHandler {
|
||||||
result[0]
|
result[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 正式报错时便不再需要报错信息了,故直接清空列表,释放内存
|
* ~~正式报错时便不再需要报错信息了,故直接清空列表,释放内存~~
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
onErrorPrepare() {
|
onErrorPrepare() {
|
||||||
|
/*
|
||||||
this.#errorList.length = 0;
|
this.#errorList.length = 0;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简易的解析报错栈堆位置信息的函数
|
||||||
|
*
|
||||||
|
* @param {string} urlLike
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
export function extractLocation(urlLike) {
|
||||||
|
// 不存在地址信息的字符串
|
||||||
|
if (!/:/.test(urlLike)) {
|
||||||
|
return [urlLike];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 捕获位置用到的正则
|
||||||
|
const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
|
||||||
|
const parts = regExp.exec(urlLike.replace(/[()]/g, ''));
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return [parts[1], parts[2] || void 0, parts[3] || void 0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('../interface/promise-error-handler').PromiseErrorHandler} PromiseErrorHandler
|
* @typedef {import('../interface/promise-error-handler').PromiseErrorHandler} PromiseErrorHandler
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue