ECMAScript proposal: Error cause (chaining errors)
source link: https://2ality.com/2021/06/error-cause.html?utm_campaign=Feed%3A+2ality+%282ality+%E2%80%93+JavaScript+and+more%29
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
In this blog post, we examine the ECMAScript proposal “Error cause” (by Chengzhong Wu and Hemanth HM). It describes a feature where instances of Error
can optionally specify that they were caused by another error.
Table of contents:
Why would we want to chain errors? #
Sometimes, we catch errors that are thrown during a more deeply nested function call and would like to attach more information to it:
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
const text = readText(filePath);
const json = JSON.parse(text);
return processJson(json);
} catch (error) {
// (A)
}
});
}
The statements inside the try
clause may throw all kinds of errors. In most cases, an error won’t be aware of the path of the file that caused it. That‘s why we would like to attach that information in line A.
How to chain errors #
The proposal enables us to do the following:
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
// ···
} catch (error) {
throw new Error(`While processing ${filePath}`, {cause: error});
}
});
}
Error
and its subclasses now have an object with options as a second parameter. The first supported option is .cause
– the error that caused the current error.
Consequence for your own code #
If you subclass Error
, it makes sense to support the second parameter with options:
class MyCustomError extends Error {
constructor(message, options) {
super(message, options);
// ···
}
}
Alternatives to the built-in support for .cause
#
AggregateError
(created by Promise.any()
) #
If Promise.any()
rejects its returned Promise, the rejection value is an instance of AggregateError
that records which (zero or more) errors caused the rejection:
class AggregateError {
constructor(errors: Iterable<any>, message: string);
get errors(): Array<any>;
get message(): string;
}
AggregateError
is a reasonable workaround if .cause
is not supported on an engine that you are targeting, however:
AggregateError
works best if we are handling multiple concurrent invocations.Error
with.cause
works best for single non-concurrent calls.
A custom error class #
The following custom error class supports chaining.
/**
* 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';
}
A library #
There are several libraries that support chaining errors. Three examples:
More information on exception handling in JavaScript #
- Chapter “Exception handling” in “JavaScript for impatient programmers”
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK