The front end has always been the closest layer to the user, and as products get better and better, we focus more on the user experience, but it’s annoying to get a lump in your throat on the front end. Why should exceptions be handled?

The front end has always been the closest layer to the user, and as products get better and better, we focus more on the user experience, but it’s annoying to get a lump in your throat on the front end.

Why should exceptions be handled?

Exceptions are uncontrollable and can affect the final rendering, but there are good reasons to do so.

1. Enhance user experience; 2. Locate the fault remotely. 3. Plan ahead and find problems early; 4. Unable to double line problems, especially mobile terminal, models and systems are problems; 5. Perfect front-end solutions and front-end monitoring system;

For JS, we only face the exception, the occurrence of the exception will not directly cause the JS engine crash, at most will only make the current execution of the task terminated.

What exceptions need to be handled?

There’s a lot of exception catching we can do on the front end. To sum up, it looks like this:

  • JSSyntax error, code exception
  • AJAXAbnormal request
  • Static resource loading is abnormal

  • Promiseabnormal
  • Iframeabnormal
  • Cross-domain Script error

  • Crash and jam

Below I explain how to handle these exceptions for each case.

Try Catch

Try-catch can catch only synchronous runtime errors, but not syntactic and asynchronous errors. 1. Synchronous runtime error:

try { let name = 'jartto'; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code

Output:

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code

2. Failed to catch the syntax error, we modify the code to delete a single quotation mark:

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code

Output:

Uncaught SyntaxError: Invalid or unexpected tokenCopy the code

However, syntax errors can be seen in our development phase and should not be smooth to the online environment.

3. Asynchronous error

try { setTimeout(() => { undefined.map(v => v); }, 1000)} Catch (e) {console.log(' catch exception: ',e); }Copy the code

Let’s take a look at the log:

Uncaught TypeError: Cannot read property 'map' of undefined 
at setTimeout (<anonymous>:3:11)Copy the code

No exceptions were caught, which is something we need to pay special attention to.

Window.onerror is not a panacea

When a JS runtime error occurs, the window raises an error event from the ErrorEvent interface and executes window.onerror().

/** * @param {String} message error message * @param {String} source error file * @param {Number} lineno line Number * @param {Number} colno column Number * @param {Object} error error Object (Object) */ window.onerror = function(message, source, lineno, colno, Error) {console.log(' caught exception: ',{message, source, lineno, colno, error}); }Copy the code

1. First try synchronizing runtime errors

Window. onerror = function(message, source, lineno, colno, error) {// message: error message (string). // source: error script URL (string) // lineno: error line number (number) // colno: error column number (number) // error: error object (object) console.log(' Exception caught: ',{message, source, lineno, colno, error}); } Jartto;Copy the code

As you can see, we caught the exception:

2. What about grammar mistakes?

Window. onerror = function(message, source, lineno, colno, error) {console.log(' Catch exception: ',{message, source, lineno, colno, error}); } let name = 'JarttoCopy the code

The console prints an exception like this:

Uncaught SyntaxError: Invalid or unexpected tokenCopy the code

What, no syntax errors caught?

3. With some trepidation, let’s finally try asynchronous runtime errors:

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


The console output:

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


4. Next, let’s try the network request exception:

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


We found that neither static resource exceptions nor interface exceptions could be caught.

A bonus: window.onerror does not raise an exception until it returns true, otherwise the console will display Uncaught Error: XXXXX even if it knows the exception occurred

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


The console will no longer have this error:

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


Note: onError is best written before all JS scripts, otherwise errors may not be caught; Onerror cannot catch syntax errors;

This is pretty clear: in practice, onError is primarily used to catch unexpected errors, while try-catch is used to monitor specific errors in predictable situations, and the combination of the two is more efficient.

What if static resource loading exceptions are not caught?

Five, the window. The addEventListener

When a resource (such as an image or script) fails to load, the element that loaded the resource raises an error Event in the Event interface and executes the onError () handler on that element. These error events do not bubble up to Windows, but (at least in Firefox) can be caught by a single Window.addeventListener.

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


Console output:

Because network request exceptions do not bubble, they must be captured in the capture phase. However, although this method can capture network request exceptions, it cannot determine whether the HTTP status is 404 or other such as 500, so it needs to cooperate with server logs to conduct investigation and analysis.

Note:

  • Returned in different browserserrorThe objects may be different and need to be compatible.
  • Take care to avoidaddEventListenerRepeat listen.

Six, Promise the Catch

Using a catch in a promise makes it very easy to catch an asynchronous error.

Errors thrown in promises without a catch cannot be caught by onError or try-catch, so it is important to remember to include a catch in a Promise to handle exceptions thrown.

To prevent missed Promise exceptions, it is recommended to add a global listener on unhandledrejection to globally listen for Uncaught Promise errors. Usage:

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


Let’s go ahead and try:

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


You can see the following output:

What if I don’t catch a Promise?

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


Well, as it turns out, it can be caught normally.

Therefore, as we said above, in order to prevent any missed Promise exceptions, it is recommended to add a global listener on unhandledrejection to globally listen for Uncaught Promise errors.

Note: To remove the exception display from the console, add:

Exception caught: ReferenceError: Nam is not defined at <anonymous>:3:15Copy the code


Seven, VUE errorHandler

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


React exception catch

React 16 provides a built-in function componentDidCatch, which is very easy to use to get React error information

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


React 16 introduces a new concept of error boundaries to help React users solve this problem.

Note that error boundaries do not catch the following errors.

1. Event handler 2. Asynchronous code 3. Server render code 4. Error in error boundaries area

For example, the class in componentDIdCatch(error,info) becomes an error boundary:

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


We then use it as a normal component:

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


The componentDidCatch() method works like the JS catch{} module, but for components, only class components of type class can be error boundaries.

In fact, in most cases we can define an Error Boundary component throughout the program and use it forever!

Iframe is abnormal

For iframe exception catching, we also need to use window.onerror:

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


A simple example might look like this:

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


Ten, Script error

In general, if an error such as Script Error occurs, it is almost certain that a cross-domain problem has occurred. At this point, there won’t be much additional information, but the solution is as follows:

Cross-source Resource Sharing mechanism (CORS) : We add the crossOrigin attribute to the Script tag.

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


Or add js scripts dynamically:

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


Access-control-allow-origin = access-Control-allow-origin = access-control-allow-origin

Alternatively, we can try this alternative way of solving Script Error:

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


A quick explanation:

  • Rewrite theEventTarget 的 addEventListenerMethods;
  • The incominglistenerWrap and return the wrappedlistenerTo execute ittry-catch;
  • Browsers don’ttry-catchUp the exception for cross-domain interception, socatchWhen it arrives, there’s a stack of information;
  • againthrowWhen you get an exception, you’re executing your domain code, sowindow.onerrorStack information is not lost when captured;

Using the wrapper addEventListener, we can also achieve the effect of “extending the stack” :

try { let name = 'jartto; console.log(nam); } catch(e) {console.log(' catch exception: ',e); }Copy the code


11. Crashes and catatons

This means that the web page is temporarily slow and JS may not be executed in time. But crash is not the same, the web page crashed, JS are not running, there is no way to monitor the crash of the web page, and report the crash?

Crashes and stalling are also important and may cause you to lose users.

1. Monitor web crashes using load and beforeUnload events of window objects. Logging Information on Browser Crashes

Uncaught SyntaxError: Invalid or unexpected tokenCopy the code


2. For the following reasons, we can use Service Worker to monitor web crashes:

  • Service WorkerIt has its own separate worker thread, separate from the web page, the web page crashes,Service WorkerIn general, it will not crash;
  • Service WorkerLife cycle is generally longer than the web page, can be used to monitor the state of the web page;
  • The website can be accessed throughnavigator.serviceWorker.controller.postMessage APITo those in charge of themselvesSWSend a message.

Error reporting

1. Send data through Ajax because Ajax requests may have exceptions and may cause cross-domain problems, dynamic creation of IMG tags is generally recommended.

2. Dynamically create an IMG tag

Uncaught SyntaxError: Invalid or unexpected tokenCopy the code


How to collect too much exception information? In practice, we have to consider the following situation: if your site is heavily visited, there will be many false messages that must be sent. In this case, we need to set the collection rate to relieve the server pressure:

Uncaught SyntaxError: Invalid or unexpected tokenCopy the code


The collection rate should be set based on the actual situation, random numbers, or certain user characteristics are good choices.

Xiii. Conclusion

Back to the question we raised at the beginning, how do you handle exceptions gracefully?

1. Add try-catch to suspicious regions. 2. Global monitoring Static resource exception window.addeventListener 4. Catch a Promise exception with no Catch: UnHandledrejection 5.VUE errorHandler and React componentDidCatch 6. Monitor web page crashes: Load and beforeUnload 7 of window objects. Cross domain crossOrigin solution

It’s quite simple, as I said above: use a composite approach to catch exceptions by type, and basically 80-90% of the problems are invisible.