Home >Web Front-end >JS Tutorial >JavaScript exception handling detailed explanation_javascript skills

JavaScript exception handling detailed explanation_javascript skills

WBOY
WBOYOriginal
2016-05-16 16:15:30937browse

Front-end engineers all know that JavaScript has basic exception handling capabilities. We can throw new Error(), and the browser will also throw an exception when we make an error when calling the API. But it is estimated that most front-end engineers have never considered collecting this abnormal information

Anyway, as long as the JavaScript error does not reappear after refreshing, the user can solve the problem by refreshing, and the browser will not crash. Just pretend that it did not happen. This assumption held true before Single Page Apps became popular. The status of the current Single Page App becomes extremely complicated after running for a period of time. The user may have performed several input operations before arriving here. Should it be refreshed when it is said to be refreshed? Shouldn’t the previous operation be completely redone? So we still need to capture and analyze these exception information, and then we can modify the code to avoid affecting the user experience.

How to catch exceptions

We wrote our own throw new Error(). Of course we can catch it if we want to, because we know exactly where throw is written. However, exceptions that occur when calling browser APIs are not necessarily easy to catch. Some APIs are written in the standards to throw exceptions, and some APIs are only thrown by individual browsers due to implementation differences or defects. For the former, we can also catch it through try-catch. For the latter, we must listen to the global exception and then catch it.

try-catch

If some browser APIs are known to throw exceptions, then we need to put the call in try-catch to prevent the entire program from entering an illegal state due to errors. For example, window.localStorage is such an API. It will throw an exception after writing data exceeds the capacity limit. This will also be the case in Safari's private browsing mode.

Copy code The code is as follows:

try {
localStorage.setItem('date', Date.now());
} catch (error) {
reportError(error);
}


Another common use case for try-catch is callbacks. Because the code of the callback function is beyond our control, we have no idea about the quality of the code or whether other APIs that will throw exceptions will be called. In order to prevent other code after calling the callback from being unable to execute due to callback errors, it is necessary to put the call back into try-catch.

Copy code The code is as follows:

listeners.forEach(function(listener) {
try {
listener();
} catch (error) {
reportError(error);
}
});


window.onerror

For areas that are not covered by try-catch, if an exception occurs, it can only be caught through window.onerror.

Copy code The code is as follows:

window.onerror =
function(errorMessage, scriptURI, lineNumber) {
reportError({
message: errorMessage,
script: scriptURI,
line: lineNumber
});
}


Be careful not to be clever and use window.addEventListener or window.attachEvent to listen for window.onerror. Many browsers only implement window.onerror, or only the implementation of window.onerror is standard. Considering that the draft standard also defines window.onerror, we can just use window.onerror.

Attributes lost

Suppose we have a reportError function that collects captured exceptions and then sends them to server-side storage in batches for query and analysis. What information do we want to collect? The more useful information includes: error type (name), error message (message), script file address (script), line number (line), column number (column), and stack trace (stack). If an exception is caught through try-catch, this information is on the Error object (supported by mainstream browsers), so reportError can also collect this information. But if it is captured through window.onerror, we all know that this event function only has 3 parameters, so the unexpected information of these 3 parameters is lost.

Serialized message

If the Error object is created by ourselves, then error.message is controlled by us. Basically, whatever we put into error.message, the first parameter (message) of window.onerror will be. (The browser will actually make slight modifications, such as adding the 'Uncaught Error: ' prefix.) Therefore, we can serialize the properties we care about (such as JSON.Stringify) and store them in error.message, and then read them in window.onerror Just take it out and deserialize it. Of course, this is limited to Error objects we create ourselves.

The fifth parameter

Browser manufacturers also know the limitations that people face when using window.onerror, so they began to add new parameters to window.onerror. Considering that only the row number but no column number seems not very symmetrical, IE first adds the column number and puts it in the fourth parameter. However, everyone is more concerned about whether they can get the complete stack, so Firefox said it would be better to put the stack in the fifth parameter. But Chrome said that it is better to put the entire Error object in the fifth parameter. You can read any properties you want, including custom properties. As a result, Chrome moved faster and implemented a new window.onerror signature in Chrome 30, which led to the standard draft being written accordingly.

Copy code The code is as follows:

window.onerror = function(
errorMessage,
scriptURI,
lineNumber,
columnNumber,
error
) {
if (error) {
reportError(error);
} else {
reportError({
message: errorMessage,
script: scriptURI,
line: lineNumber,
column: columnNumber
});
}
}


Attribute regularization

The names of the Error object attributes we discussed before are based on Chrome’s naming method. However, different browsers name the Error object attributes differently. For example, the script file address is called script in Chrome but is called filename in Firefox. . Therefore, we also need a special function to normalize the Error object, that is, to map different attribute names to unified attribute names. For specific methods, please refer to this article. Although browser implementations will be updated, maintaining such a mapping table manually is not too difficult.

Similar to the stack trace format. This attribute saves a stack of exception information in the form of plain text. Since the text format used by each browser is different, it is also necessary to maintain a regular expression manually to extract the function of each frame from the plain text. Name (identifier), file (script), line number (line) and column number (column).

Security restrictions

If you have also encountered an error with the message 'Script error.', you will understand what I am talking about. This is actually a browser limitation for script files from different origins. The reason for this security restriction is this: assuming that the HTML returned by an online bank after a user logs in is different from the HTML seen by anonymous users, a third-party website can put the URI of the online bank into the script.src attribute. Of course, HTML cannot be parsed as JS, so the browser will throw an exception, and the third-party website can determine whether the user is logged in by parsing the location of the exception. For this reason, the browser filters all exceptions thrown by script files from different sources until only a single unchanged message like 'Script error.' is left, and all other attributes disappear.

For websites of a certain scale, it is normal for script files to be placed on a CDN with different sources. Now even if you build your own small website, common frameworks such as jQuery and Backbone can directly reference the versions on public CDNs to speed up user downloads. So this security restriction does cause some trouble, causing the exception information we collect from Chrome and Firefox to be useless 'Script error.'

CORS

To bypass this restriction, just ensure that the script file and the page itself have the same origin. But putting the script file on a server that is not accelerated by CDN will not slow down the user's download speed? One solution is to continue placing the script file on the CDN, use XMLHttpRequest to download the content back through CORS, and then create a <script> tag to inject it into the page. The code embedded in the page is of course from the same source. </p> <p>This sounds simple, but there are many details to implement. To use a simple example: </p> <p></p> <div class="codetitle"> <span><a style="CURSOR: pointer" data="85881" class="copybut" id="copybut85881" onclick="doCopy('code85881')"><u>Copy code</u></a></span> The code is as follows:</div> <div class="codebody" id="code85881"> <br> <script src="<a href="http://cdn.com/step1.js"></script">http://cdn.com/step1.js"></script</a>><br> <script><br> (function step2() {})();<br> </script>

data-src="http://cdn.com/step3.js"
data-line-start="2001"
>
// 'n' * 2000
// code for step 3



After this processing, if the error.line of an error is 3005, it means that the actual error.script should be 'http://cdn.com/step3.js', and the actual error.line should be 5 . We can complete this line number reverse check in the reportError function mentioned earlier.

Of course, since we cannot guarantee that each script file has only 1000 lines, and some script files may be significantly less than 1000 lines, there is no need to allocate a fixed range of 1000 lines to each