Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the right granularity of error catching in JavaScript?

I used to put a try...catch inside 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
};

This way, keeping a code interface relatively simple, I thought any error inside my code would have been caught and logged/managed.

var myInstance = new MyConstructor();

Instead, is it enough one global catch block per script? Caring to throw each possible (or noteworthy) error, it seems to me enough to know every error occurring in the app:

// 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
        (e.g. client-side, via console, or to the server...)
    */
}

I searched and read threads on Stackoverflow and remarkable resources on line about JavaScript error management.
In this phase I am interested in finding the best way to find all the errors more than managing them for a graceful behaviour of the user interface.
Isn't it the right approach? I am open to any suggestion.

like image 834
yodabar Avatar asked Jan 14 '13 10:01

yodabar


People also ask

What is catch error in JavaScript?

JavaScript try and catch The try statement allows you to define a block of code to be tested for errors while it is being executed. The catch statement allows you to define a block of code to be executed, if an error occurs in the try block.

Should you throw errors in JavaScript?

It's best to avoid throwing errors from inside a Promise, because they may not always be caught, depending on how the code that called them is structured. However it's good practice to return an error when rejecting a Promise, and you can return Error custom types just like any other Error.


2 Answers

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

The important thing is to remember that when you write a piece of code you in fact write a contract describing of how bits and pieces of code interact. For example in your case when you create a MyConstructor instance why would it fail? What promise is made when it is generated? Most importantly, who should deal with it failing?

Some examples

Let's say that for example we have a class Car and instances of Car have a method drive(x).

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

The action drive(x) might fail, for example if the Car is already on the edge of the screen, or Car has no fuel.

We just defined drive as "the Car moves x places to the right" which means that the car expects to be able to complete the drive action and being unable to complete it is logically an exception. In this case it is quite obvious that the one handling the exception is the caller of drive and not the car itself since it wouldn't have to know what environment it is driving on. Even better, the caller should not attempt to drive the car off the edge or without fuel any way.

In another case

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

We might have some edge case we did not think of in our moveCars() method, or one that is not supposed to happen due to some assumption, however the user to environment expects it to contain all the move logic which means when an exception occurs it should deal with it itself.

So, what is the general strategy?

You should handle exceptions based on the responsibility the component running the code has.

like image 133
Benjamin Gruenbaum Avatar answered Sep 28 '22 04:09

Benjamin Gruenbaum


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

Three important questions about contracts are: What does contract expect? What does contract guarantee? What does contract maintain? For an example, assume function divide(a, b) returns a / b. This function expects that b is nonzero(precondition), if b happens to be zero, this function should throw exception instead of catch exception. Because it's the caller's responsibility to guarantee the validity of passing argument, which is the part of the contract. Other than that, all other errors that might happen in the divide function should be caught inside, because that's its own responsibility. As the other part of the contract, divide promises to return a value (i.e. quotient) which multiplied by b should equal to a(that's called postcondition).

Exception-handling can be complex. A function(or module) may choose to catch an exception if that's within its responsibility, or choose not to catch if that's beyond its duty, or choose to catch the exception first, process it, and then rethrow to the caller after wrapping the exception as caller-layer's exception.

To sum up, exception-handling is not a stand-alone problem, it's part of whole system's design. Once a designer reasonably separates a complicated system into relatively simple modules, each with good abstraction/interface and well-defined contract/responsibility, how and where to handle exception should be self-explanatory.

like image 24
Hui Zheng Avatar answered Sep 27 '22 04:09

Hui Zheng