Merge pull request #807 from nofficalfs/Dev-Fix-ErrorHandler
fix: error handle.
This commit is contained in:
commit
bab68de606
|
@ -1,36 +1,49 @@
|
|||
/**
|
||||
* 关于`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}
|
||||
*/
|
||||
export class ChromePromiseErrorHandler {
|
||||
|
||||
/**
|
||||
* 用于临时记录报错信息的列表,通过`Error.prepareStackTrace`更新该列表
|
||||
* ~~用于临时记录报错信息的列表,通过`Error.prepareStackTrace`更新该列表~~
|
||||
*
|
||||
* @type {[Error, NodeJS.CallSite[]][]}
|
||||
* 现在用于存储报错过的错误信息
|
||||
*
|
||||
* @type {Error[]}
|
||||
*/
|
||||
#errorList;
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
/*
|
||||
this.#errorList = [];
|
||||
this.#originErrorPrepareStackTrace = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = (error, stackTraces) => {
|
||||
|
@ -42,13 +55,19 @@ export class ChromePromiseErrorHandler {
|
|||
this.#errorList.push([error, stackTraces]);
|
||||
return result;
|
||||
};
|
||||
*/
|
||||
this.#errorList = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将原来可能的`Error.prepareStackTrace`赋值回去
|
||||
* ~~将原来可能的`Error.prepareStackTrace`赋值回去~~
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
onUnload() {
|
||||
/*
|
||||
Error.prepareStackTrace = this.#originErrorPrepareStackTrace;
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,6 +79,61 @@ export class ChromePromiseErrorHandler {
|
|||
*/
|
||||
onHandle(event) {
|
||||
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)
|
||||
const result = this.#errorList.find(savedError => savedError[0] === error);
|
||||
if (result) {
|
||||
|
@ -72,17 +146,42 @@ export class ChromePromiseErrorHandler {
|
|||
result[0]
|
||||
);
|
||||
}
|
||||
*/
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 正式报错时便不再需要报错信息了,故直接清空列表,释放内存
|
||||
* ~~正式报错时便不再需要报错信息了,故直接清空列表,释放内存~~
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
onErrorPrepare() {
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue