Proper error handling in JavaScript

Proper error handling in JavaScript

Error handling is a mandatory part of any major computer program. In a web application, typically this is a server-side issue. Every error that occurs, data access or manipulation is usually classified and logged.

Error handling in has slowly been adopted on the browser side of web application as well even though is equally important for the user experience. Of course, you can always rely on the browser to handle the errors in JS, but this has its downfalls. Browser may treat a type of error different and the feedback to the user could vary from an explicit error message to nothing. The user may get frustrated and try again or even leave your side for ever.

In this article, we are going to go through to types of error that could appear in the JS code and how they can be handled.


Error types in Javascript

There are several error types that can occur in JavaScript. Each inherit the behavior from the general Error. The Error object is rarely, if ever, thrown by a browser. It’s main purpose is the allow developer throw custom errors.

RangeError 

A RangeError occurs a parameter or a value is outside of its valid range. For example, calling   arr = new Array(-4) will throw an error.

Note: A common confusion is to expect an error like this to be thrown when we are trying to access an index that does not exist in an array.

var a = [1, 2, 3];
console.log(a[4]); // undefined
console.log(a[-2]); // undefined

As you may know, arrays are simply objects in Javascript which store their items as properties with name 0, 1, 2 and so on. So trying to access a[4] like in this example is similar to accessing a property that does not exist in an object, so the result is undefined.

If you want to know more about how arrays work in JavaScript, check out my article which covers this topic – Javascript Arrays – Tips, tricks and examples -.

ReferenceError

A reference error is thrown you try to assign a undeclared reference to a variable.

var myObj = obj; // throws ReferenceError when x it's not declared

EvalError

Thrown when an exception occurs while using the eval() function. There are plenty of arguments for not using ever the eval() function in a web app (which are outside of this scope) so, ideally, you will never encounter it. However, it’s good to know that it exists.

SyntaxError

Similar to EvalError, you can encounter syntax errors when using eval function. Passing a string that could not be parsed as valid JS code is the reason for that error.

eval("var var a;");

TypeError

TypeError is one of the most frequently encountered errors in JS and occurs when a variable os of an unexpected type or an attempt is made to access a nonexistent method. This can occur for any number of reasons most often when a type-specific operation is used with a variable of the wrong type. Here are some examples:

var o = new 10; 
alert("name" in true); 
Function.prototype.toString.call("name");

UriError

Uri errors occur when using encodeURI() or decodeURI() with a malformed URI.


The try and catch statements

Introduced in the third version of Javascript, the try-catch statement behaves very similar to other mainstream languages.

try {
  // insert statements that could potentially throw an error here 
} catch(error) {
  // handling errors 
}

Any code that could potentially throw an error should be place in the try statement. If and only if an error occurs, the code inside the catch statement is executed.

Handle multiple errors

In many other languages, a try block can be paired with multiple catch statements if you want to add different behaviors for some type of errors. In JavaScript this is not possible but there are workarounds around that by catching the exception as a general error and checking the type via instanceof

try {
    someFunction(); 
} catch(error) { 
   if (error instanceof TypeError) {
       // handle TypeError
   } else if (error instanceof ReferenceError) { 
       // handle ReferenceError
   } else {
       // handle all other errors 
   }
}

The finally clause

The try-catch statement can be used with another block – the finally block -. The finally clause is added at the end of the try-catch and the code inside it is executed no mater what happens in the try-catch.

try {
    someFunction();
} catch(error) { 
    // some error handling
} finally {
    return 0; 
}

You may ask yourself: Why do I need finally? Why can’t I just return 0 after the catch statement? That is a fair question.

Think about it this why: no matter what happens in the try-catch statement, the finally block will always execute.  If the catch or try block throws an error or even returns a value, the finally statement will still be executed.

If a finally statement is used, using catch becomes optional (only one of the 2 is mandatory to be used in pair with a try).

finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a returncontinue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

The finally block is a key tool for preventing resource leaks. When closing a file or otherwise recovering resources, place the code in a finally block to ensure that resource is always recovered.

function myFunction() {
    try {
        throw Error("Error");
    } catch(error) {
        console.log("Hello from catch! " + error.message);
        return 2; 
    } finally {
        console.log("Hello from finally");
        return 1; 
    }
}

console.log(myFunction());
// "Hello from catch! Error"
// "Hello from finally"
// 1

The above function returns 1, even though we reach a return 2 statement in catch block.

Remember:

  • When an error occurs within the try block, the browser considers the error to have been handled, and so it won’t report it
  • Use a try-catch statement when an error might occur that is out of your control (ex. function that is part of a JS library, that function may throw errors on purpose or by mistake)
  • Throwing and catching error is a powerful mechanism to transmit messages between different layers of the application. Avoid using it if this is not the case (for example, if a function expects a number as parameter and receive a string, it’s better to validate the param instead throwing an error

Throwing errors

A developer has the possibility to manually throw custom errors at any point during the application lifecycle. There are not restrictions regarding the error, basically any data type can be used alongside with the keyword throw.

throw 2; 

throw {message: "Ups"} ;

throw false;

All this are valid errors. When the throw operator is used, code execution stops immediately and continues only if a try-catch statement catches the value that was thrown.

Browser errors can be simulated using the build-in error types (discussed above). The only property of those errors that is reliable in all browsers is message.

throw SyntaxError("What do you mean?"); 

throw RangeError("You're stepping out of bounds here");

throw TypeError("Sorry, you're not my type");

Developers can implement their own custom errors for handling specific behavior. Throwing custom errors is a great way to provide more information about why a function has failed. Errors should be thrown when a particular known error condition exists that won’t allow the function to execute properly.


When to throw and when to catch

In general, errors are thrown in the low levels of an application architecture, when there is not enough information that could explain the error so it can’t be really handled. For example, a utility library used in a wrong way may throw errors with a detailed explanation about what went wrong. In this case, it the responsibility of the caller to handle that exception.

The best way to think about the difference between throwing errors and catching errors – defined by Nicholas Zakas – is this:

The purpose of catching an error is to prevent the browser from responding in its default manner; the purpose of throwing an error is to provide information about why an error occurred.


Debugging techniques

Debugging main purpose is to provide to the developer data about the behavior of the application in order to help him/her to identify an error as fast as possible. Since Javascript main engine is the browser, this aspect, like many others are a bit tricky. Common strategies included displaying an alert every time something went wrong or even showing the message on screen. This techniques are extremely inefficient at development and a real danger in production. If they are forgot there, they may create an unpleasant experience.


Use the console object in development

Console object is present in all modern browser and is harmless to the user experience. You can log any message directly from code and see them in the browser’s developer tools. Most browsers provide more functions that behave the same but have a different level of gravity – error(message), info(message), log(message), warn(message).

 


Log important errors to a server in production

A common practice in web applications is to have a centralized log system to collect informations about the user behavior on the site. This is helpful for marketing purposes – knowing what part of your site users enjoy more – but also for collecting data about certain errors. The server will have to expose and endpoint for solely this purpose.

 


Final words

Thank you for your time! Leave your questions or your feedback bellow.

If you found this helpful, please subscribe for more weekly web wisdom!

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s