Home >Web Front-end >JS Tutorial >A Hidden Gem in JavaScript Debugging: error.cause
What is the biggest challenge in debugging? One of them is undoubtedly tracing the source of errors.
Imagine this scenario:
const func = () => { doSth('A'); doSth('B'); };
When func throws an error, how do you identify at which step the error occurred? Was it caused by doSth('A'), doSth('B'), or func itself? Clearly, the error lacks sufficient context.
A common approach to address this issue might look like this:
const func = () => { try { doSth('A'); } catch (error) { throw new Error('An error from A', error); } try { doSth('B'); } catch (error) { throw new Error('An error from B', error); } };
With this approach, you can locate the source of the error more easily. However, this solution has several limitations:
Loss of error details:
If the error contains extensive information (e.g., payloads, HTTP status codes, error codes), this approach only adds the error message of doSth to the newly constructed error. Other crucial details, including the original stack trace, are lost.
Decreased log readability:
With more than two potential error points, the logs can become cluttered and difficult to interpret.
Ambiguity in expressing intent:
The code does not explicitly communicate that the new error is caused by the specific doSth function being caught, leaving room for improved code readability.
To address these issues, ECMAScript 2022 introduced error.cause.
This feature allows developers to specify the root cause of an error when creating a new error object. By using error.cause, you can establish a chain of errors, making it easier to debug and trace the root cause of an issue.
Here’s a simple example:
try { // Some operation that may throw an error } catch (error) { throw new Error('Something went wrong', { cause: error }); }
With this approach, you can build causal links between errors. For instance:
const func = () => { try { doSth('A'); } catch (error) { throw new Error('An error from A', { cause: error }); } try { doSth('B'); } catch (error) { throw new Error('An error from B', { cause: error }); } };
This allows us to catch errors thrown by lower-level functions (e.g., doSth('A')), throw a new error that adds relevant context (e.g., "An error occurred while executing doSth('A')"), and preserve the original error details (e.g., "A is an illegal argument.").
Another advantage of error.cause is its ability to create a chain of linked errors, enabling developers to trace issues back through multiple layers of the application:
const func = () => { try { try { try { doSth('A'); } catch (error) { throw new Error('Error at depth 3', { cause: error }); } } catch (error) { throw new Error('Error at depth 2', { cause: error }); } } catch (error) { throw new Error('Error at depth 1', { cause: error }); } }; console.log(error.cause.cause); // Error at depth 3
In Node.js, errors with a cause are handled specially in the console. All related error stacks are printed:
const func = () => { doSth('A'); doSth('B'); };
const func = () => { try { doSth('A'); } catch (error) { throw new Error('An error from A', error); } try { doSth('B'); } catch (error) { throw new Error('An error from B', error); } };
This approach not only improves error traceability but also enhances the readability and maintainability of your code.
Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:
Multi-Language Support
Deploy unlimited projects for free
Unbeatable Cost Efficiency
Streamlined Developer Experience
Effortless Scalability and High Performance
Explore more in the Documentation!
Follow us on X: @LeapcellHQ
Read on our blog
The above is the detailed content of A Hidden Gem in JavaScript Debugging: error.cause. For more information, please follow other related articles on the PHP Chinese website!