如何在JavaScript中链接异常(即像Java中那样添加原因)

insrf1ej  于 2023-06-28  发布在  Java
关注(0)|答案(7)|浏览(92)

有没有一个标准/最佳实践的方法来添加JavaScript中的异常原因。在java中,你可以这样做:

Throwable t = new Exception("whatever");
t.addCause(previouslyCaughtException);
throw t;

当输出结果异常时,它会给予您一个很好的跟踪,包括原因。在JavaScript中有什么好的方法来做到这一点吗?或者我必须自己动手?

6ojccjat

6ojccjat1#

现在(直到有更好的答案),这是我所做的:

...
} catch(e) {
  throw new Error("My error message, caused by: "+e.stack+"\n ------The above causes:-----")
}

我打印异常的方式使它看起来漂亮而干净:

console.log(e.stack)

打印如下内容:

My error message: SomeError
<some line>
<more lines>
------The above causes:-----
<some line>
<more lines>

如果这一行写“causes”可能会更好,因为首先打印的是导致错误的异常的堆栈跟踪。

ccgok5k5

ccgok5k52#

如果你使用Node.js,你可以使用VError
Joyent还有一个页面讨论Node.js https://www.joyent.com/developers/node/design/errors中错误处理的最佳实践
唯一的问题是,如果你传入一个null或未定义的参数,我不喜欢Joyent的VError福尔斯。这在处理错误时尤其重要,因为它会掩盖问题的根本原因。我已经fork了他们的VError,所以它不会因为null或未定义的参数而失败。https://github.com/naddison36/node-verror

vktxenjb

vktxenjb3#

在prod中,我们使用TraceError

用法

import TraceError from 'trace-error';

global.TraceError = TraceError; // expose globally (optional)

throw new TraceError('Could not set status', srcError, ...otherErrors);

输出

kqqjbcuj

kqqjbcuj4#

在NodeJS中,由于v8版本9.3,现在可以做到这一点,并且它已经在NodeJS版本16.9.0中引入
www.example.com

try {
  aFunctionThatWillThrow()
} catch (err) {
  throw new Error('A function that will throw did throw!', { cause: err });
}
pftdvrlh

pftdvrlh5#

tl;dr这不是一个解决方案,只是一个帮助,直到ECMA脚本采用一些标准。

编辑:我把这个答案打包到chainable-error npm包中。
这是一个很难的主题。原因是ECMA脚本定义中没有关于堆栈跟踪的定义(甚至在ES 9/ES 2019中也没有)。因此,一些引擎实现了自己的堆栈跟踪及其表示方法。
他们中的许多人已经实现了Error.prototype.stack属性,这是堆栈跟踪的字符串表示。由于这是没有定义的,你不能依赖于字符串格式。幸运的是,V8引擎很常见(Google Chrome和NodeJS),这让我们有机会至少尝试一下。
V8(以及使用它的应用程序)的一个优点是Stack跟踪具有通用格式:

/path/to/file/script.js:11
        throw new Error("Some new Message", e);
        ^

Error: Some new Message
    at testOtherFnc (/path/to/file/script.js:69:15)
    at Object.<anonymous> (/path/to/file/script.js:73:1)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3)

...且控制台不解析和样式化堆栈跟踪。

这给了我们一个很好的机会来链接它们(或者至少改变错误生成的输出)。
一个非常简单的方法是这样的:

let ff = v => JSON.stringify(v, undefined, 4);
const formatForOutput = v => {
    try {
        return ff(v).replace(/\n/g, '\n    ');
    } catch (e) {
        return "" + v;
    }
};

const chainErrors = exporting.chainErrors = (e1, e2) => {
    if (e1 instanceof Error)
        e2.stack += '\nCaused by: ' + e1.stack;
    else
        e2.stack += '\nWas caused by throwing:\n    ' + formatForOutput(e1);

    return e2;
}

你可以这样使用:

function someErrorThrowingFunction() {
    throw new Error("Some Message");
}

function testOtherFnc() {
    try {
        someErrorThrowingFunction();
    } catch (e) {
        throw chainErrors(e, new Error("Some new Message"));
    }
}

生产:

/path/to/file/script.js:11
        throw new Error("Some new Message", e);
        ^

Error: Some new Message
    at testOtherFnc (/path/to/file/script.js:11:15)
    at Object.<anonymous> (/path/to/file/script.js:15:1)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3)
Caused by: Error: Some Message
    at someErrorThrowingFunction (/path/to/file/script.js:4:11)
    at testOtherFnc (/path/to/file/script.js:9:9)
    at Object.<anonymous> (/path/to/file/script.js:15:1)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)

这与Java生成的堆栈跟踪非常相似。这有三个问题。
第一个问题是呼叫站点的重复,这是可以解决的,但很复杂。
第二个是生成的输出是依赖于引擎的,这种尝试对于V8来说很好,但对于Firefox来说就不好用了,因为Firefox不仅使用了另一种风格,而且还解析和设计了错误消息,这阻止了我们像这样链接它。
第三个问题是可用性。这是一个有点笨重,你必须记住这个功能,你需要保持跟踪,如果你在正确的引擎。另一种方法是这样的:

const Error = (() => {
    const glob = (() => { try { return window; } catch (e) { return global; } })();

    const isErrorExtensible = (() => {
        try {
            // making sure this is an js engine which creates "extensible" error stacks (i.e. not firefox)
            const stack = (new glob.Error('Test String')).stack;
            return stack.slice(0, 26) == 'Error: Test String\n    at ';
        } catch (e) { return false; }
    })();

    const OriginalError = glob.Error;

    if (isErrorExtensible) {
        let ff = v => JSON.stringify(v, undefined, 4);
        const formatForOutput = v => {
            try {
                return ff(v).replace(/\n/g, '\n    ');
            } catch (e) {
                return "" + v;
            }
        };

        const chainErrors = (e1, e2) => {
            if (e1 instanceof OriginalError)
                e2.stack += '\nCaused by: ' + e1.stack;
            else
                e2.stack += '\nWas caused by throwing:\n    ' + formatForOutput(e1);

            return e2;
        }

        class Error extends OriginalError {
            constructor(msg, chained) {
                super(msg);

                if (arguments.length > 1)
                    chainErrors(chained, this);
            }
        }

        return Error;
    } else
        return OriginalError; // returning the original if we can't chain it
})();

然后你可以像在Java中一样:

function someErrorThrowingFunction() {
    throw new Error("Some Message");
}

function testOtherFnc() {
    try {
        someErrorThrowingFunction();
    } catch (e) {
        throw new Error("Some new Message", e);
    }
}

testOtherFnc();

即使第二个版本带来了一些(其他)问题,它可能是“更容易”的一个,因为即使引擎不支持链接,你也不需要更改代码,因为你可以给予一个函数(错误构造函数)尽可能多的参数。
无论如何,希望这将是ES 2020的一部分。

nimxete2

nimxete26#

您可以链接错误对象Error,执行stackmessage的concat。

var console = {
    log: function(s) {
      document.getElementById("console").innerHTML += s + "<br/>"
    }
  }

var error1=new Error("This is error 1");
console.log("Message: ".concat( error1.message ));
console.log("Stack<br>".concat(error1.stack) );

var error2=new Error("This is error 2");
console.log("Message: ".concat( error2.message) );
console.log("Stack<br>".concat( error2.stack) );

var error3=new Error("This is error 3");
error3.stack=error3.stack.concat(error2.stack).concat(error1.stack)
console.log("Message: ".concat(error3.message));
console.log("Stack<br>".concat(error3.stack));
<div id="console" />
r9f1avp5

r9f1avp57#

截至2021年底,所有主要浏览器都实现了Error的新cause属性,可用于异常链。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
这篇文章的用法示例:

try {
  connectToDatabase();
} catch (err) {
  throw new Error("Connecting to database failed.", { cause: err });
}

相关问题