It is difficult for the code to run exactly as the developer wants, and unexpected situations always emerge. It is obviously not a qualified developer’s job to ignore them. It is particularly important to deal with, collect and analyze error information, and this article discusses this part.

For the front-end students, errors tend to block the program and throw an error, giving the user a very bad experience. If we can prepare for errors in advance and react to errors caught, we can give our users a better experience. By collecting and analyzing error messages, you can proactively discover potential code problems, so that you don’t have to wait for the user to go around and tell you about the bug.

Client Collection

window.onerror

Window. onError is raised globally when a JavaScript runtime error or syntax error occurs.

window.onerror = (msg, url, lineNum, colNum, err) = > {
  console.log('Exception message for which the error occurred (string) :${msg}`)
  console.log('Error occurred at script URL (string) :${url}`)
  console.log('Line number (number) where the error occurred:${lineNum}`)
  console.log('Column number (number) where the error occurred:${colNum}`)
  console.log('Error objects where errors occur (Error objects) :${err}`)};Copy the code

Note: here we get thrown errors that have not been caught. Instead of making mistakes like promise.

Everything is not smooth. When many students try again, they must find that they can only get a Script error without the error itself, such as message, URL, etc. LineNum and colNum are also 0, which is not the error message when the real error occurs.

The reason is that browsers are restricted by the same origin policy. For security reasons, if an exception is thrown in a Script referenced by a page that is not in the same domain, the page does not have permission to obtain the exception details and displays Script Error. In Chrome, there is a security mechanism that does not expose the full cross-domain error message to you, only in Chrome, Firefox, Safari can get the full error message.

To solve the Script error

To solve this problem, you can use cross-source resource sharing (CORS)

  1. Adds the Crossorigin attribute to the script tag on the page.
<! If you add the crossorigin attribute, the browser will automatically add an Origin field to the request header to tell the server where it came from.
<script src="http://xxx.xxx.xxx.x/xxx.js" crossorigin></script>
Copy the code
  1. Add access-Control-Allow-Origin to the response header to support cross-domain resource sharing.

You can determine whether you need to deal with this problem according to your own requirements and collect incomplete error information.

unhandledrejection

As mentioned earlier, errors in promises cannot be tried… Catch and window.onerror. At this point we need unhandledrejection to help us catch this part of the error.

window.addEventListener('unhandledrejection', (e) => {
  console.log('Promise.reject(), which tells you why the error occurred:${e.reason}`);
  console.log(` Promise object:${e.promise}`);
});
Copy the code

It is worth mentioning that the compatibility of Unhandledrejection is not very good. A picture of Caniuse is attached below

console.error

Console. error is often referred to as the log that is printed, the error that is expected, the error that has been caught, the content that has been processed. So they tend to be ignored.

It’s always common to see code that does a lot of things with a big try… Error. Maybe for exception handling this is done, you caught the error and didn’t crash the program, but it’s an integral part of error collection

  try {
    // some code
  } catch (err) {
    console.error(err)
  }
Copy the code

So modify console.error a little bit so that every time console.error is raised we can do something like report to the error collection system.

console.error = (func= > {
  return (. args) = > {
    // This is where console.error errors can be collected
    // Do something
    func.apply(console, args);
  }
})(console.error);
Copy the code

addEventListener(‘error’)

A big man pointed out my inadequacy of this piece at a glance, came down to learn, add this piece of content up. Thank @ Dikaplio 🙏

On the client side, some static resources, images, CSS, script, failed to load. None of the methods mentioned above can be captured.

Method 1: onError capture

<script src="https://cdn.xxx.com/js/test.js"  onerror="errorHandler(this)"></script>

<link rel="stylesheet" href="https://cdn.xxx.com/styles/test.css" onerror="errorHandler(this)">
Copy the code

This can catch the errors of static resources, but the disadvantages are also obvious. It is not a good way to invade the code.

Method 2: addEventListener(‘error’)

In most cases addEventListener(‘error’) works just as well as window.onerror. There are two event mechanisms in the browser, catch and bubble, which are used to fetch an error.

But for a resource loading error event, canBubble: false, so window.onError does not get the resource loading error, whereas addEventListener does. But once you get an error, you need to make a simple distinction between a resource loading error and another error, because this method can also catch syntax errors and other errors.

Resource loading errors do not have a message field, so simply run the code before all resources to catch the error.

window.addEventListener('error', (errorEvent) => {
    console.log(errorEvent)
    cosnole.log(errorEvent.message)
}, true)
Copy the code

Notice that we’re getting an event here, not an error object like we did before.

Server Collection

Collecting on the Node server is essentially the same as collecting on the client, with some methodological differences.

uncaughtException

Catch any unhandled errors through Node’s global processing, which is the last level, bottom-of-the-envelope operations that often crash if left unhandled.

process.on('uncaughtException', err => {
  //do something
});
Copy the code

unhandledRejection

In Node, errors in promises also cannot be tried… Catch and uncaughtException catch. At this point we need unhandledRejection to help us catch this part of the error.

process.on('unhandledRejection', err => {
  //do something
});
Copy the code

console.error

console.error = (func= > {
  return (. args) = > {
    // This is where console.error errors can be collected
    // Do something
    func.apply(console, args);
  }
})(console.error);
Copy the code

Exception handling with framework (koA as an example)

For the Node side, we can usually catch errors with the help of the framework. For example, KOA can catch errors on the framework layer through app. On error. It also catches errors that are not caught inside, like promise errors.

app.on('error', (err, ctx) => {
  // do something
});
Copy the code

It is worth mentioning that we can actively raise this error event within the framework, even if we have already caught the processing of the error, will continue to throw the framework layer, easy to do a lot of unified processing.

ctx.app.emit('error', err, ctx);
Copy the code

Summary of error types

  1. Synchronization error => Can be detected by 1. Try… Onerror 3.process.on(‘uncaughtException’)

  2. Async errors => such as setInterval, async functions that are not await, etc., will not be tried… Onerror and process.on(‘uncaughtException’).

  3. Promise Error => promise.reject (new Error(‘some wrong’)); Promise errors such as these are not caught by window.onError and process.on(‘uncaughtException’), nor by a try… Process. on(‘unhandledRejection’) and window.addeventListener (‘unhandledRejection’)

Note: locally try… A caught error is not thrown upwards, so a global catch is definitely not caught, unless the catch is completed and the error is thrown upwards.

Unified handling of exceptions

Overall idea: In the business layer to capture the error packaging continue to throw to the upper layer, in the packaging, all the errors are inherited from our own definition of the error class, in the error class there are a lot of our own custom error type, when throwing just need to simply throw the instance of the error type, At the end of the middleware, we can catch all the errors and deal with them uniformly. The errors are classed, classed, and some may not have been caught before, so you can do a lot here.

Defining error classes

class SystemError extends Error {
  constructor(message) {
    super(message);
    // Error type
    // Error level
    // Error message
    // ...
  }
  static wrapper(e) {
    const error = new this(e.message);
    // Wrap various things on e onto error
    returnerror; }}// Common errors can be defined in advance
createDBError(xxx) {
  const sysError = SystemError.wrapper(error);
  // Write an error message
  // Write an error type
  // Write error level
  // ...
  return sysError;
}

// This way, when throwing errors in the business, it only needs to be simple
throw createDBError(error, { someInfo });
Copy the code

Error trapping

Catch errors as accurately as possible in the business, grade and classify them, and then continue to throw them to the upper level.

Because errors are accurately caught, it’s easy to create a lot of tries… In the case of catch nesting, we want to avoid such bloated code as much as possible

  try {
    try {
      // Operate the database
    } catch (err) {
      throw createDBError(error, { someInfo });
    }
    try {
      // Normal business
    } catch (err) {
      throwcreateBusinessError(error, { someInfo }); }}catch (err) {
    throw err
  }
Copy the code

There must be something wrong with our code, and we need to think about whether we can split it and not create such a bloated situation.

Middleware unified processing

Because all of the previous errors were wrapped and reported, in the uppermost middleware, we could handle all errors uniformly.

  1. All the errors we wrap are from our custom classes, and we can easily determine which errors we already know and which we have never caught.
  2. You can respond to requests and render pages more friendly depending on the type of error.
  3. Based on the error level, you can determine which errors need to be collected and which errors need to be reported.

conclusion

I have been dealing with all kinds of errors for a while, and I hope that we can share our harvest with you in the future when handling exceptions.