No matter how technical you are, errors or anomalies are part of the life of the application developer. The discontinuity of web development leaves many places where errors can occur and that do occur. The key to the solution is to handle any unforeseen (or foreseeable) error to control the user's experience. With JavaScript, there are a variety of technical and linguistic features that can be used to correctly solve any problem.
Handling errors in JavaScript is dangerous. If you believe in Murphy's law, mistakes will go wrong! In this article, I'll delve into the error handling in JavaScript. I will involve some traps and good practice. Finally, we'll discuss asynchronous code processing and Ajax.
I think the JavaScript event-driven model adds a rich meaning to the language. I think the event-driven engine of this browser is no different from the error mechanism. Whenever an error occurs, it is the equivalent of throwing an event at a point in time. Theoretically, we can handle error-shooting events in JavaScript as we do with ordinary events. If this sounds strange to you, focus your attention and start learning the following journey. This article is for JavaScript only for clients.
Example
The code examples used in this article are available on GitHub, which is what the page looks like today:
Clicking each button throws an error. It simulates the generation of a TypeError-type exception. The following is a definition of such a module and a unit test.
Function error () {
var foo = {};
return Foo.bar ();
}
First, this function defines an empty object, Foo. Please note that the bar () method is not defined anywhere. We use unit tests to verify that this does cause an error.
It (' throws a TypeError ', function () {
should.throws (target, typeerror);
});
This unit test uses the test assertions in the Mocha and should.js libraries. Mocha is a running test framework, Should.js is an assertion library. If you are unfamiliar, you can browse their documents online for free. A test case usually starts with it (' description ') and ends with an assertion in should or failure. The advantage of using this framework is that you can do unit testing in node without having to be in the browser. I recommend that you take these tests seriously because they validate many of the key basic concepts in JavaScript.
As shown above, the error () defines an empty object and then attempts to invoke the method in it. Because the bar () method does not exist in this object, it throws an exception. Believe me, in a dynamic language like JavaScript, it's possible for anyone to make such a mistake.
A bad demonstration.
Let's take a look at the bad error-handling method first. I handle the wrong action abstracted out and bound to the button. The following are the unit tests of the handler:
function Badhandler (FN) {
try {return
fn ();
} catch (e) {} return
null;
}
This handler receives a callback function, FN, as a dependency. The function is then called inside the handler. This unit test demonstrates how to use this method.
It (' Returns a value without errors ', function () {
var fn = function () {return
1;
};
var result = target (FN);
Result.should.equal (1);
});
It (' Returns a null with Errors ', function () {
var fn = function () {
throw error (' random error ');
var result = target (FN);
should (result). equal (null);
As you can see, if an error occurs, this bizarre processing returns a null. The callback function, FN (), points to a legitimate method or error. The following click Processing event completes the rest of the section.
(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));
The bad thing is that I just got a null. It made me very confused when I was trying to figure out what was going wrong. This silent policy of error overrides all aspects of the design from the user experience to data corruption. The depressing side of it all was that I had to spend hours debugging but I couldn't see the bugs in the Try-catch code block. This bizarre processing hides all the errors in the code and assumes that everything is normal. This can be done smoothly in some teams that do not pay attention to code quality. However, these hidden errors will eventually force you to spend hours debugging the code. In a multi-tier solution that relies on the call stack, it is possible to determine where the error comes from. It is possible that in rare cases it is appropriate for Try-catch to do fault-silent processing. But it's not a good plan to deal with mistakes.
This failure, the silent strategy, will prompt you to do a better job of making mistakes in your code. JavaScript provides a more elegant way to handle this type of problem.
Difficult to read scheme
Go on, and then look at the way it's not very well understood. I will skip the part that is tightly coupled with the DOM. This part is no different from the bad treatment we just saw. The emphasis is on the part of the following unit test that handles the exception.
function Uglyhandler (FN) {
try {return
fn ();
} catch (e) {
throw error (' A new error ');
} It (' Returns a new error with errors ', function () {
var fn = function () {
throw new TypeError (' type error ');
};
Should.throws (function () {
target (FN);
}, Error);
});
There is a good progress compared to the just bad way of handling it. The exception is thrown in the call stack. My favorite place is to be free from the stack, which is a great help for debugging. Throws an exception, and the interpreter finds the next handler in a level-by-level view of the call stack. This provides many opportunities to handle errors at the top of the call stack. Unfortunately, because he was a mistake that was not very well understood, I could not see the original error message. So I have to go along the call stack and find the original exception. But at least I knew there was an error in the place where the exception was thrown.
This difficult to read error handling is harmless but makes the code difficult to understand. Let's see how the browser handles the error.
Call stack
One way to throw an exception is to add a Try...catch code block at the top level of the call stack. For example:
function Main (bomb) {
try {
bomb ();
} catch (e) {
//Handle All the error things
}
}
But remember I said that browsers are event-driven? Yes, an exception in JavaScript is just an event. The interpreter stops the program at the current context where the exception occurred and throws an exception. To confirm this, here is a global event handler function onerror that we can see. It looks just like this:
Window.addeventlistener (' Error ', function (e) {
var error = E.error;
Console.log (error);
This event handler captures errors in the execution environment. Error events can produce various errors in a variety of places. The focus of this approach is to centrally handle errors in your code. Like other events, you can use a global handler to handle a variety of different errors. This makes error handling only a single goal if you comply with SOLID (single responsibility sole duty, open-closed, Liskov substitution substitution, interface segregation interface Separation and dependency inversion-dependent inversion) principle. You can register the error handling function at any time. The interpreter loops through the functions. The code is freed from statements filled with try...catch and becomes easy to debug. The key to this approach is to handle the errors that occur as you do with JavaScript normal events.
Now, there's a way to display the call stack with a global handler, what can we do with it? After all, we're going to take advantage of the call stack.
Record the call stack
The call stack is useful for handling bug fixes. The good news is that the browser provides this information. Even now, the stack property of the Error object is not standard, but this property is generally supported in newer browsers.
So the cool thing we can do is print it out to the server:
Window.addeventlistener (' Error ', function (e) {
var stack = e.error.stack;
var message = e.error.tostring ();
if (stack) {message
+ = ' \ n ' + stack;
}
var xhr = new XMLHttpRequest ();
Xhr.open (' POST ', '/log ', true);
Xhr.send (message);
It may not be obvious in the code example, but this event handler is triggered by the preceding error code. As mentioned above, each handler has a single purpose, which makes the code DRY (don ' t repeat yourself does not repeat the manufacture of the wheel). What I am interested in is how to capture these messages on the server.
Here's a screenshot of the node Runtime:
The call stack is helpful for debugging code. Never underestimate the role of the call stack.
Asynchronous processing
Oh, it's pretty dangerous to handle asynchronous code! JavaScript brings asynchronous code out of the current execution environment. This means that there is a problem with the following Try...catch statement.
function Asynchandler (FN) {
try {
settimeout (function () {
fn ();
}, 1);
} catch (e) {}
}
This unit test has the remaining parts:
It (' does not catch exceptions with errors ', function () {
var fn = function () {
throw new TypeError (' type error ') );
};
Failedpromise (function () {
target (FN);
}). Should.be.rejectedWith (TypeError);
});
function Failedpromise (FN) {return
new Promise (function (resolve, reject) {
reject (FN);}
);
I have to use a promise to end this handler to verify the exception. Note that although my code is in Try...catch, an unhandled exception has occurred. Yes, Try...catch only works in a single execution environment. When an exception is thrown, the interpreter's execution environment is not the current Try-catch block. This behavior is similar to an Ajax call. So now there are two options. An alternative scenario is to catch an exception in an asynchronous callback:
settimeout (function () {
try {
fn ();
} catch (e) {
//Handle This async error
}
}, 1);
Although this method is useful, there is still a lot of room for improvement. First, the Try...catch code block appears everywhere in the code. In fact, in the 70 's the programming called, they wanted their code to be able to rollback. In addition, the V8 engine does not encourage the use of Try...catch code blocks in functions (V8 is the JavaScript engine used by chrome browsers and Node). They recommend a block of code that writes these catch exceptions at the top of the call stack.
So, what does this tell us? As I said above, the global error handler in any execution context is necessary. If you add an error handler to the Window object, that means you've done it! is it good to obey the principles of DRY and SOLID? A global error handler will keep your code readable and clean.
The following is a report of server-side exception handling printing. Note that if you use the code in the example, the output may be slightly different depending on the browser you are using.
This handler can even tell me which error originated from the asynchronous code. It tells me that the error comes from the settimeout () handler function. It's so cool!
Errors are part of every application, but proper error handling is not. There are at least two ways of handling errors. One is the scheme of failure that is silent, that is, ignoring errors in code. The other is a quick way to find and fix errors, that is, to stop and reproduce at the wrong place. I think I have made it clear which one I agree with and why I am in favour of it. My choice: Do not hide the problem. No one will blame you for the unexpected events in your program. This is acceptable, to interrupt the point, reproduce, give the user a try. In an imperfect world, it is important to give yourself a chance. Mistakes are unavoidable, and what you do is important in order to solve the mistakes. The proper use of JavaScript's error handling features and automatic and flexible decoding can make the user's experience smoother and make diagnostics easier for the developer.