在这篇博文中,我们研究了ECMAScript提案 "错误原因"(由Chengzhong Wu和Hemanth HM提出)。它描述了一个功能,即Error 的实例可以选择性地指定它们是由另一个错误引起的。
为什么我们要把错误连在一起呢?
有时,我们会捕捉到在更深嵌套的函数调用过程中抛出的错误,并希望将更多的信息附加到它上面:
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
const text = readText(filePath);
const json = JSON.parse(text);
return processJson(json);
} catch (error) {
// (A)
}
});
}
try 子句里面的语句可能会抛出各种各样的错误。在大多数情况下,错误不会知道引起它的文件的路径。这就是为什么我们想在A行附上这些信息。
如何连锁错误
该提案使我们能够做到以下几点:
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
// ···
} catch (error) {
throw new Error(`While processing ${filePath}`, {cause: error});
}
});
}
Error 和它的子类现在有一个以选项为第二参数的对象。第一个支持的选项是 - 导致当前错误的错误。.cause
对你自己的代码的后果
如果你对Error 进行子类化,那么用选项来支持第二个参数是有意义的:
class MyCustomError extends Error {
constructor(message, options) {
super(message, options);
// ···
}
}
替代内置的对.cause 的支持
AggregateError (由 创建)Promise.any()
如果 Promise.any()拒绝其返回的Promise,拒绝值是一个AggregateError 的实例,它记录了哪些(零个或多个)错误导致了拒绝:
class AggregateError {
constructor(errors: Iterable<any>, message: string);
get errors(): Array<any>;
get message(): string;
}
AggregateError 然而,如果你的目标引擎不支持 ,这就是一个合理的变通方法:.cause
AggregateError如果我们要处理多个并发的调用,则效果最好。Error用 ,对单个非并发调用效果最好。.cause
一个自定义的错误类
下面的自定义错误类支持链式:
/**
* This subclass of Error supports chaining.
* If available, it uses the built-in support for property `.cause`.
* Otherwise, it sets it up itself.
*
* @see https://github.com/tc39/proposal-error-cause
*/
class CausedError extends Error {
constructor(message, options) {
super(message, options);
if ((isObject(options) && 'cause' in options) && !('cause' in this)) {
const cause = options.cause;
this.cause = cause;
if ('stack' in cause) {
this.stack = this.stack + '\nCAUSE: ' + cause.stack;
}
}
}
}
function isObject(value) {
return value !== null && typeof value === 'object';
}
一个库
有几个库支持链式错误,三个例子:
- Node.js和浏览器:node-common-errors
- 仅限Node.js:node-verror
- 仅限Node.js:composite-error