JavaScript exception handling Detailed _javascript tips

Source: Internet
Author: User
Tags anonymous exception handling

Front-end engineers know that JavaScript has basic exception handling capabilities. We can throw the new error () and the browser throws an exception when we invoke the API error. But it is estimated that most front-end engineers have not considered collecting these exception information.

Anyway, as long as the JavaScript error after the refresh is no longer, the user can solve the problem by refreshing, the browser will not crash, when it did not happen well. This assumption was established before the single Page App was popular. Now the single Page APP runs for a period of time after a very complex state, the user may have a number of input operations to come here, said refresh on the refresh Ah? Do you want to redo the previous operation? So we still need to capture and analyze these exception information, and then we can modify the code to avoid impacting the user experience.

How exceptions are caught

We wrote the throw New Error () to capture, of course, because we know exactly where throw is written. However, the exception that occurs when invoking the browser API is not necessarily so easily captured, and some APIs are written in the standard to throw exceptions, and some APIs only individual browsers throw exceptions because of differences or flaws. For the former we can also capture through Try-catch, for which we must listen for global exceptions and then capture them.

Try-catch

If some browser APIs are known to throw exceptions, then we need to put the call inside the Try-catch to avoid an error causing the entire program to enter an illegal state. For example, Window.localstorage is an API that throws an exception when the write data exceeds the capacity limit, as is the case in Safari's privacy browsing mode.

Copy Code code as follows:

try {
Localstorage.setitem (' Date ', Date.now ());
catch (Error) {
ReportError (Error);
}


Another common Try-catch scenario is the callback. Because the code for the callback function is not controllable, the quality of the code will not invoke any other API that throws an exception, we don't know. In order not to be able to execute other code after invoking the callback because of a callback error, it is necessary to return the call to the Try-catch.

Copy Code code as follows:

Listeners.foreach (function (listener) {
try {
Listener ();
catch (Error) {
ReportError (Error);
}
});


Window.onerror

For Try-catch, an exception can only be captured by Window.onerror if it is not covered.

Copy Code code as follows:

Window.onerror =
function (errormessage, Scripturi, linenumber) {
ReportError ({
Message:errormessage,
Script:scripturi,
Line:linenumber
});
}


Be careful not to use Window.addeventlistener or window.attachevent in the form of listening to Window.onerror. Many browsers only implement Window.onerror, or only WINDOW.ONERROR implementations are standard. Taking into account the definition of the draft standard is also Window.onerror, we use window.onerror just fine.

property is missing

Suppose we have a reporterror function to collect the caught exception and then bulk send it to the server-side store for query analysis, so what information do we want to collect? Useful information is the type of error (name), error message, script file address (scripts), line numbers (lines), column numbers (columns), stack traces (stack). If an exception is captured through Try-catch, the information is on the Error object (supported by mainstream browsers), so ReportError can collect the information as well. But if captured through Window.onerror, we all know that this event function has only 3 parameters, so the unexpected information for these 3 parameters is lost.

Serialization message

If the Error object is created by ourselves, then the error.message is controlled by us. Basically what we put into the error.message, the Window.onerror's first argument (message) is what. (browsers can actually make minor changes, such as adding ' uncaught Error: ' prefix.) So we can serialize the attributes we're focusing on (for example, JSON). stringify) is then deposited into the error.message and then deserialized on Window.onerror read. Of course, this is limited to the Error object we created ourselves.

Fifth parameter

Browser vendors are also aware of the limitations of using window.onerror, so start adding new parameters to the Window.onerror. In view of the fact that only line numbers do not appear to be symmetrical, IE first add the column number and place the fourth parameter. However, we are more concerned about the ability to get the full stack, so Firefox said to put the stack on the fifth parameter bar. But Chrome said it would be better to put the entire Error object on the fifth argument, and what attributes you want to read, including custom attributes. As a result of the faster chrome action, the new Window.onerror signature was implemented in Chrome 30, which led to the standard drafts being written accordingly.

Copy Code code 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 Normalization

The Error object properties that we discussed earlier, whose names are based on the Chrome naming method, differ in how different browsers name the properties of the error object, such as script file addresses that are called scripts in chrome but are called filename in Firefox. Therefore, we also need a special function to normalize the Error object, that is, to map different attribute names to the uniform attribute names. The specific procedure can refer to this article. Although the browser implementation is updated, it is not too difficult for a human to maintain such a mapping.

Similar to the format of stack traces (stacks). This property preserves the stack information of an exception when it occurs in plain text, because the text format used by each browser is different, so it also requires manual maintenance of a regular expression used to extract each frame's function name (identifier), file (script) from plain text, Line number and column number (column).

Security restrictions

If you've also encountered the message ' script error. ', you'll understand what I'm talking about, which is actually the browser's restrictions on different source (origin) script files. The reason for this security restriction is this: Suppose a network of silver in the user login after the return of HTML and anonymous users see the HTML is not the same, a Third-party Web site will be able to put the net silver URI into the script.src attribute. HTML of course can not be used as JS parsing, so the browser will throw an exception, and this Third-party Web site will be able to resolve the abnormal location to determine whether the user has logged in. This browser for different source script files thrown by the exception to filter, filtering only the "script error." Such a unchanged message, all the other attributes disappear.

For a certain size of the site, the script file on the CDN, not homologous is very normal. Now, as a small web site, common frameworks such as jQuery and backbone can directly reference the version on the public CDN to speed up user downloads. So this security constraint does cause some trouble, and the exception information we collect from Chrome and Firefox is useless ' Script error. '

CORS

To circumvent this limitation, just make sure that the script file and the page itself are homologous. But is the script file on the server without CDN acceleration, does not reduce the user download speed? One solution is that the script files continue to be on the CDN, using XMLHttpRequest to download the content back through CORS, and then create <script> tag into the page. The code embedded in the page is of course homologous.

This is simple to say, but there are a lot of details to realize. In a simple example:

Copy Code code as follows:

<script src= "Http://cdn.com/step1.js" ></script>
<script>
(function Step2 () {}) ();
</script>
<script src= "Http://cdn.com/step3.js" ></script>


We all know that this step1, STEP2, step3 if there is a dependency, you must strictly follow this order, or you may be wrong. Browsers can request step1 and step3 files in parallel, but the order of execution is guaranteed. If we get the contents of Step1 and step3 through xmlhttprequest ourselves, we need to ensure that the order is correct. In addition, don't forget to Step2, step1 in the form of non-blocking download Step2 can be executed, so we must also artificially intervene step2 let it wait for Step1 to complete and then execute.

If we already have a complete set of tools to generate <script> labels on different pages on the site, we need to adjust the tool to make changes to <script> tags:

Copy Code code as follows:

<script>
Scheduleremotescript (' http://cdn.com/step1.js ');
</script>
<script>
Scheduleinlinescript (function code () {
(function Step2 () {}) ();
});
</script>
<script>
Scheduleremotescript (' http://cdn.com/step3.js ');
</script>


We need to implement both the Scheduleremotescript and Scheduleinlinescript functions and make sure they are defined before the first <script> tag that references the external script file, and then the remaining < The script> label will be rewritten as the above form. Note that the STEP2 function, which was originally executed immediately, was placed inside a larger code function. The code function is not executed, it is just a container, so that the original step2 can be preserved without escaping, but will not be executed immediately.

Next, we need to implement a complete set of mechanisms to ensure that the content of the files downloaded from Scheduleremotescript by the address and the code obtained directly from Scheduleinlinescript can be executed one after the other in the correct order. Detailed code I am not here to give, we are interested to be able to achieve their own.

Line number Reverse Check

Getting content through CORS and then injecting code into the page can break the security limit, but introduces a new problem, which is line number conflict. It was possible to navigate to a unique script file via Error.script and then navigate to a unique line number via Error.line. Now because the page is embedded code, a number of <script> tags can not be differentiated by error.script, however, each <script> tag inside the line number is from 1, The result is that we can't use the exception information to locate the source code where the error resides.

To avoid line number collisions, we can waste a number of line numbers so that the line number intervals used by the actual code in each <script> tag do not overlap. For example, assuming that the actual code in each <script> tag is no more than 1000 lines, I can have the code in the first <script> tag occupy the 1–1000 line so that the code in the second <script> tag occupies the 1001–2000 Line (insert 1000 blank lines before), the third <script> label code occupies the first 2001–3000 line (insert 2000 blank lines before), and so on. We then use the data-* attribute to record this information for easy retrieval.

Copy Code code as follows:

<script
Data-src= "Http://cdn.com/step1.js"
data-line-start= "1"
>
Code for Step 1
</script>
<script data-line-start= "1001" >
' \ n ' * 1000
Code for Step 2
</script>
<script
Data-src= "Http://cdn.com/step3.js"
Data-line-start= "2001"
>
' \ n ' * 2000
Code for Step 3
</script>


After this treatment, if a wrong error.line is 3005, that means 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 in the previous mentioned ReportError function to check the work.

Of course, because we have no way to guarantee that each script file is only 1000 lines, it is possible that some script files are significantly less than 1000 lines, so there is no need to allocate 1000 lines of the interval to each <script> tag. We can allocate intervals based on the number of actual script lines, as long as the intervals used by each <script> label are guaranteed to overlap.

Crossorigin Property

The browser's security restrictions for different sources of content are certainly not limited to <script> tags. Since XMLHttpRequest can break through this limitation through CORS, why is the resource referenced directly through the tag not available? Of course this is possible.

Restrictions on <script> tags referencing different source script files also work on tags referencing different source picture files. If a label is a different source, once used in <canvas> drawing, the <canvas> will become a write-only state, ensuring that the site cannot steal unauthorized different source image data through JavaScript. Later tags solved the problem by introducing the Crossorigin attribute. If you use crossorigin= "anonymous", it is equivalent to an anonymous CORS, and if you use ' crossorigin= ' use-credentials, it is equivalent to a CORS with authentication.

Since tags can do this, why can't <script> tags do that? The browser vendor then adds the same crossorigin attribute to the <script> tag to address the security restrictions mentioned above. Now the Chrome and Firefox support for this property is completely fine. Safari treats crossorigin= "anonymous" as crossorigin= "Use-credentials", and the result is that Safari will fail as a certificate if the server only supports anonymous CORS. Because the CDN server is designed to return only static content because of its performance, it is impossible to dynamically return the required HTTP Header,safari of the authentication CORS according to the request equivalent to not being able to use this feature to solve the above problem.

Summarize

JavaScript exception handling looks simple, but it's really not easy to capture the exception and then analyze the attributes, but it's not that different from other languages. Now, although there are third-party services that provide a Google Analytics service to capture JavaScript exceptions, it is necessary to make sure that the details and principles are made in your own hands.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.