What is the correct granularity of errors in JavaScript?

I used try...catch in any method of my JS classes:

 var MyConstructor = function() { this.init = function() { try { // the method code... } catch(error) { // the error manager log actions } }; // other methods, with the same try/catch usage }; 

Thus, while keeping the code interface relatively simple, I thought that any error inside my code would be caught and logged / managed.

 var myInstance = new MyConstructor(); 

Instead, is a single global catch block per script sufficient? Taking care of every possible (or noteworthy) error, it seems to me, it is enough to know every error that occurs in the application:

 // no try...catch inside the classes, but only a global try...catch per script: try { var myInstance = new MyConstructor(); } catch(error) { /* log in the needed environment (eg client-side, via console, or to the server...) */ } 

I searched and read Stackoverflow threads and great resources in the queue for managing JavaScript errors.
At this point, I am interested in finding a better way to find all the errors more than managing them for the graceful user interface behavior.
Isn't that the right approach? I am open to any suggestion.

+6
source share
2 answers

The rule of thumb is that you should ask yourself: "Who should deal with the problem logically?" and stick to it.

It is important to remember that when you write a piece of code, you are actually writing a contract describing the interaction of bits and pieces of code. For example, in your case, when you create an instance of MyConstructor, why doesn't it work? What promise was made after its creation? Most importantly, who should handle this unsuccessfully?

Some examples

Say, for example, we have a class Car , and Car instances have a drive(x) method.

When you call drive(x) , Car moves x to the right.

The drive(x) action may fail, for example, if Car already on the edge of the screen or Car does not contain fuel.

We simply defined the drive as "the car moves x places to the right", which means that the car expects to be able to complete the drive and could not complete it, a logical exception. In this case, it is obvious that the one that handles the exception is the caller, not the car itself, because it does not need to know in which environment it works. Moreover, the caller should not attempt to drive a car from the edge or without fuel.

Otherwise

Let's say that in the same example, Environment is a class that contains cars, and it has a moveCars() method that moves all cars in the environment according to some internal logic. Environment used by a program that expects it to contain all the logic of movement. We use some algorithm in moveCars() , which should assure us that cars do not collide.

We may have some extreme case that we did not think about in our moveCars() method, or one that should not happen due to some assumption, but the user on Wednesday expects him to contain all the movement logic , which means when an exception occurs, it must deal with itself.

So what is the overall strategy?

You should handle exceptions based on the responsibility performed by the code component.

+2
source

This question is actually not limited to JavaScript. The correct granularity of the exception traversal depends on the correct granularity of the modules and functions that potentially have an exception. A well-designed module or function should be tied to a clearly defined contract ( DbC ). As long as the contract is explicitly established, the problem of handling exceptions will become much simpler.

Three important questions about contracts: what awaits the contract? What guarantees the contract? What supports the contract? For example, suppose the function divide(a, b) returns a / b . This function expects b non-zero ( precondition ), if b turns out to be zero, this function should throw an exception instead of a catch exception. Because it is the responsibility of the caller to ensure the fairness of the transmitted argument, which is part of the contract. Other than that, all other errors that may occur in the divide function must be inside because it is his own responsibility. As another part of the divide contract, to return a value (i.e. a Coefficient) that is multiplied by b , should equal a (which is called postcondition ).

Exception handling can be complicated. A function (or module) may choose to catch an exception if it is within its responsibility, or not want to catch it if it is outside the scope of its responsibility, or first try to catch the exception, handle it, and then throw it to the caller after wrapping exceptions as a caller level exception.

To summarize, exception handling is not a separate issue; it is part of the whole system design. As soon as a designer intelligently separates a complex system from relatively simple modules, each of which has a good abstraction / interface and a clearly defined contract / responsibility, how and where to handle the exception should be understandable.

+1
source

All Articles