JavaScript’s event-driven paradigm adds to the richness of the language and makes programming in JavaScript even more diverse. If you think of the browser as an event-driven tool for JavaScript, an event will be thrown when an error occurs. In theory, these errors can be considered simple events in JavaScript.

This article discusses error handling in client-side JavaScript. This article mainly introduces the contents of error prone, error handling and asynchronous code writing in JavaScript.

Let’s take a look at how to properly handle errors in JavaScript.

 

The Demo presentation

The demo used in this article can be found on GitHub and will look like this when run:

Each button raises an Exception that simulates a TypeError that was thrown. Here is the definition of a module:

// scripts/error.js
function error() {
  var foo = {};
  return foo.bar();
}Copy the code

First, this function declares an empty object, foo. Note that bar() is not defined anywhere. Next verify that the unit test raises an “error” :

// tests/scripts/errorTest.js
it('throws a TypeError', function() { should.throws(error, TypeError); });Copy the code

This unit test is in Mocha and has a test declaration in should.js. Mocha is the test run tool, while should.js is the assertion library. This unit test runs on Node and does not require a browser.

Error () defines an empty object and then attempts to access a method. Because bar() does not exist within the object, an exception is thrown. This kind of error happens to a dynamic language like JavaScript, and it happens to everyone!

 

Error Handling (1)

The above error is handled with the following code:

// scripts/badHandler.js
function badHandler(fn) {
  try {
    returnfn(); }catch (e) { }
  return null;
}Copy the code

The handler takes FN as an input parameter, and fn is called inside the handler. Unit tests will show what the above error handlers do:

// tests/scripts/badHandlerTest.js
it('returns a value without errors', function() {
  var fn = function() {
    return 1; };var result =badHandler(fn); result.should.equal(1); }); it('returns a null with errors', function() {
  var fn = function() {
    throw new Error('random error'); };var result =badHandler(fn); should(result).equal(null);
});Copy the code

If there is a problem, the error handler returns NULL. The fn() callback can point to a valid method or error.

The following click events continue event processing:

// scripts/badHandlerDom.js
(function (handler, bomb) {
  var badButton = document.getElementById('bad');
  if(badButton) { badButton.addEventListener('click', function() { handler(bomb); console.log('Imagine, getting promoted for hiding mistakes'); }); } }(badHandler, error));Copy the code

This approach hides an error in the code that is hard to spot. Hidden bugs can take hours of debugging. Especially in multi-tier solutions with deep call stacks, this error can be harder to find. So this is bad error handling.

 

Error Handling (2)

Here’s another error handling.

// scripts/uglyHandler.js
function uglyHandler(fn) {
  try {
    returnfn(); }catch (e) {
    throw new Error('a new error'); }}Copy the code

The exception handling method is as follows:

// tests/scripts/uglyHandlerTest.js
it('returns a new error with errors', function () {
  var fn = function () {
    throw new TypeError('type error'); }; should.throws(function() { uglyHandler(fn); }, Error); });Copy the code

The above is a significant improvement in error handling. Here the exception calls the stack to bubble up. At the same time, errors expand the stack, which is very helpful for debugging. In addition to throwing exceptions, the interpreter looks for additional processing along the stack. This also opens up the possibility of handling errors from the top of the stack. But this is still poor error handling, requiring us to trace the original exception step by step down the stack.

An alternative is to end poor error handling with a custom error approach. This can be helpful when you add more detail to the error.

Such as:

// scripts/specifiedError.js
// Create a custom error
var SpecifiedError = function SpecifiedError(message) {
  this.name = 'SpecifiedError';
  this.message = message || '';
  this.stack = (new Error()).stack;
};
SpecifiedError.prototype = new Error();
SpecifiedError.prototype.constructor = SpecifiedError;Copy the code

// scripts/uglyHandlerImproved.js
function uglyHandlerImproved(fn) {
  try {
    returnfn(); }catch (e) {
    throw newSpecifiedError(e.message); }}Copy the code

// tests/scripts/uglyHandlerImprovedTest.js
it('returns a specified error with errors', function () {
  var fn = function () {
    throw new TypeError('type error'); }; should.throws(function() { uglyHandlerImproved(fn); }, SpecifiedError); });Copy the code

The specified error adds more details and preserves the original error message. With this improvement, the above treatment is no longer a bad treatment, but a clear and useful one.

After the above processing, we also received an unhandled exception. Let’s take a look at what the browser can do to help with errors.

 

A stack

One way to handle exceptions is to add a try at the top of the call stack… The catch.

Such as:

function main(bomb) {
  try{ bomb(); }catch (e) {
    // Handle all the error things
}}Copy the code

However, browsers are event-driven, and an exception in JavaScript is also an event. When an exception occurs, the interpreter pauses execution and expands:

// scripts/errorHandlerDom.js
window.addEventListener('error', function (e) {
  var error =e.error; console.log(error); });Copy the code

This event handler catches any errors that occur in the execution context. Error events at each target trigger various types of errors. This concentration of error handling in code is very aggressive. You can use Daisy chain handling to handle specific errors. If you follow the SOLID principle, you can adopt single-purpose error handling. These handlers can be registered at any time, and the interpreter loops through the handlers that need to be executed. The code base can be accessed from the try… The catch block is released, which also makes debugging easier. In JavaScript, it is important to treat error handling as an event.

 

Capture the stack

The call stack can be very useful when solving problems, and the browser can provide this information. Stack properties are not part of the standard, but modern browsers can already view this information.

Here is an example of logging an error on the server:

// scripts/errorAjaxHandlerDom.js
window.addEventListener('error', function (e) {
  var stack = e.error.stack;
  var message = e.error.toString();
  if(stack) { message+= '\n' +stack; }var xhr = newXMLHttpRequest(); xhr.open('POST', '/log', true);
  // Fire an Ajax request with error details
  xhr.send(message);
});Copy the code

Each error handling has a single purpose, which keeps your code DRY (single purpose, don’t repeat yourself).

In the browser, you need to add event handling to the DOM. This means that if you are building a third-party library, your events will coexist with the client code. Window.addeventlistener () will handle this for you without erasing any existing events.

Here’s a screenshot of the log on the server:

Logs can be viewed at a command prompt, but on Windows, logs are non-dynamic.

The logs make it clear what conditions triggered what errors. The call stack can also be very useful when debugging, so don’t underestimate its power.

In JavaScript, error messages only apply to a single field. Because you will not see any error details when using scripts from non-domains.

One solution is to rethrow the error while preserving the error message:

try {
  return fn();
} catch (e) {
  throw new Error(e.message);
}Copy the code

Once the error backup is restarted, the global error handler does the rest. Make sure your error handling is in the same domain, which preserves the original message, stack, and custom error objects.

 

Asynchronous processing

When JavaScript runs asynchronous code, there is a problem with the following exception handling:

// scripts/asyncHandler.js
function asyncHandler(fn) {
  try {
    // This rips the potential bomb from the current context
    setTimeout(function() { fn(); },1); }catch (e) { }
}Copy the code

Unit tests to see the problem:

// tests/scripts/asyncHandlerTest.js
it('does not catch exceptions with errors', function () {
  // The bomb
  var fn = function () {
    throw new TypeError('type error'); };// Check that the exception is not caught
  should.doesNotThrow(function() { asyncHandler(fn); }); });Copy the code

This exception was not caught and we verified it with unit tests. Although the code contains try… Catch, but try… A catch statement can only work in a single execution context. When an exception is thrown, the interpreter has already left the try… Catch, so the exception is not handled. The same thing happens with Ajax calls.

So, one solution is to catch exceptions in asynchronous callbacks:

setTimeout(function () {
  try{ fn(); }catch (e) {
    // Handle this async error
}},1);Copy the code

This works better, but there is still a lot of room for improvement.

First, these try… The catch block is entangled throughout the region. In fact, the V8 browser engine discourages the use of try inside functions… The catch block. V8 is the JavaScript engine used in the Chrome browser and Node. One way to do this is to try… Catch blocks move to the top of the call stack, but this does not apply to asynchronous code programming.

Since global error handling can be performed in any context, adding a window object for error handling ensures DRY and SOLID principles for your code. Global error handling also keeps your asynchronous code clean.

Here’s how the exception handling is reported on the server. Note that the output varies from browser to browser.

As you can see from the error handling, the error comes from the setTimeout() function of the asynchronous code.

 

conclusion

In error handling, do not hide problems, but discover them in time, and use various methods to trace the root of the problem in order to solve the problem. While there are always mistakes to be made when writing code, there is no need to be too ashamed of them. What we need to do now is to solve the problem and avoid bigger problems.

The original link: https://www.sitepoint.com/proper-error-handling-javascript/

Reproduced please indicate from: Grape city control

 

About Grape Town

Grape City is the global industry leader in controls, the world’s leading provider of customized tools for enterprise applications, enterprise reporting and business intelligence solutions, serving more than 75% of the global Fortune 500 companies.

 

Related reading:

More than 100 sets of report templates are free to download

1 minute to choose the JavaScript framework that works best for you

A chart shows you the seven most popular JavaScript framework features

Top 10 JavaScript editors, which one are you using?