JS Error Handling

Handling Errors

Sometimes your JavaScript code doesn't run as expected, resulting in errors. There can be various reasons for these errors, such as:

  • Network connection issues
  • Invalid user input in form fields
  • Referencing non-existent objects or functions
  • Incorrect data sent to or received from the server
  • Temporary unavailability of required services

These errors, occurring while the script is running, are known as runtime errors. A well-designed application should handle such errors gracefully by providing clear and informative messages to the user.

The try...catch Statement

JavaScript provides the try-catch statement to catch and handle runtime errors gracefully:

Place the code that may cause an error inside the try block. If an error occurs, code execution jumps to the catch block, where you can handle the error appropriately:

try {
    // Code that may cause an error
} catch(error) {
    // Action to handle the error
}

If no error occurs in the try block, the catch block is skipped, and the program continues executing after the try-catch statement.

Here's an example to demonstrate how the try-catch statement works:

try {
var greet = "Hi, there!";
document.write(greet);

// Trying to access a non-existent variable
document.write(welcome);

// If error occurred following line won't execute
alert("All statements are executed successfully.");
} catch(error) {
// Handle the error
alert("Caught error: " + error.message);
}

// Continue execution
document.write("<p>Hello World!</p>");

The script above will display the error in an alert dialog box instead of printing it to the browser console. Additionally, the program continues executing despite the occurrence of an error.

It's important to note that the catch keyword is followed by an identifier in parentheses. This identifier acts like a function parameter. When an error occurs, the JavaScript interpreter creates an object containing details about the error. This error object is then passed as an argument to catch for handling.

Tip: Using the try-catch statement in JavaScript helps manage exceptions, which are signals indicating unexpected conditions or errors during program execution. The terms "exception" and "error" are often used interchangeably.


The try...catch...finally Statement

The try-catch statement in JavaScript can include a finally clause. Code inside the finally block executes regardless of whether an error occurs in the preceding try block or not.

Here's an example that always shows the total time taken to complete the execution:

// Assigning the value returned by the prompt dialog box to a variable
var num = prompt("Enter a positive integer between 0 to 100");

// Storing the time when execution start
var start = Date.now();

try {
if(num > 0 && num <= 100) {
alert(Math.pow(num, num)); // the base to the exponent power
} else {
throw new Error("An invalid value is entered!");
}
} catch(e) {
alert(e.message);
} finally {
// Displaying the time taken to execute the code
alert("Execution took: " + (Date.now() - start) + "ms");
}

Throwing Errors

Previously, we've seen errors that are automatically raised by the JavaScript parser when an issue occurs. However, you can also intentionally throw an error using the throw statement.

The syntax for the throw statement is straightforward: throw expression;

The expression can be an object or any value of any data type. It's advisable to use objects, particularly those with name and message properties. The JavaScript built-in Error() constructor provides a convenient way to create such error objects. Here are a few examples:

throw 123;
throw "Missing values!";
throw true;
throw { name: "InvalidParameter", message: "Parameter is not a number!" };
throw new Error("Something went wrong!");

Note: When using JavaScript's built-in error constructor functions (e.g., Error(), TypeError(), etc.) to create error objects, the name property corresponds to the name of the constructor, and the message property equals the argument passed to the constructor function.

We're about to define a function called squareRoot() to calculate the square root of a number. Normally, you'd use JavaScript's built-in Math.sqrt() function for this purpose. However, a drawback is that it returns NaN (Not a Number) for negative numbers, without clarifying what went wrong.

To address this issue, we'll implement a solution where a custom error is thrown if a negative number is provided.

function squareRoot(number) {
// Throw error if number is negative
if(number < 0) {
throw new Error("Sorry, can't calculate square root of a negative number.");
} else {
return Math.sqrt(number);
}
}

try {
squareRoot(16);
squareRoot(625);
squareRoot(-9);
squareRoot(100);

// If error is thrown following line won't execute
alert("All calculations are performed successfully.");
} catch(e) {
// Handle the error
alert(e.message);
}

Tip: Theoretically, the square root of negative numbers can be computed using imaginary numbers, where i is defined as i2 = -1. For example, the square root of -4 would be 2i, and 3i for -9. However, JavaScript does not support imaginary numbers.


Error Types

The Error object serves as the foundational type for all errors in JavaScript. It includes two primary properties: name, which specifies the type of error, and message, which provides a detailed description of the error.

JavaScript programs can encounter various types of errors during execution. Common ones include RangeError, ReferenceError, SyntaxError, TypeError, and URIError.

The next section provides an in-depth explanation of each of these error types:

RangeError

A RangeError occurs when a number is used outside the permissible range of values. For instance, attempting to create an array with a negative length will trigger a RangeError.

var num = 12.735;
num.toFixed(200); // throws a range error (allowable range from 0 to 100)

var array = new Array(-1); // throws a range error

ReferenceError

A ReferenceError usually happens when attempting to access a variable or object that hasn't been defined or doesn't exist. Below is an example illustrating when a ReferenceError occurs.

var firstName = "Harry";
console.log(firstname); // throws a reference error (variable names are case-sensitive)

undefinedObj.getValues(); // throws a reference error

nonexistentArray.length; // throws a reference error

SyntaxError

A SyntaxError occurs during runtime when there's a syntax issue in your JavaScript code. This could happen if a closing bracket is missing, loops are incorrectly structured, or similar problems arise.

var array = ["a", "b", "c"];
document.write(array.slice(2); // throws a syntax error (missing bracket)

alert("Hello World!'); // throws a syntax error (quote mismatch)

TypeError

A TypeError is triggered when a value does not match the expected type. This can occur when attempting to use a string method on a number, invoking an array method on a string, or similar mismatches.

var num = 123;
num.toLowerCase(); /* throws a type error (since toLowerCase() is a string method, a number can't be converted to lowercase) */

var greet = "Hello World!"
greet.join() // throws a type error (since join() is an array method)

URIError

A URIError occurs when an invalid Uniform Resource Identifier (URI) is provided to URI-related functions like encodeURI() or decodeURI(). This error is thrown to indicate issues with URI handling in JavaScript.

var a = "%E6%A2%B";
decodeURI(a);  // throws a URI error

var b = "\uD800";
encodeURI(b);   // throws a URI error

Note: Another error type, EvalError, was historically thrown when an error occurred during code execution using the eval() function. However, JavaScript no longer throws this error, though the object remains for backward compatibility purposes.

The specific error types can also be manually thrown using their respective constructors and the throw statement. For instance, to throw a TypeError, you can utilize the TypeError() constructor as shown:

var num = prompt("Please enter a number");

try {
if(num != "" && num !== null && isFinite(+num)) {
alert(Math.exp(num));
} else {
throw new TypeError("You have not entered a number.");
}
} catch(e) {
alert(e.name);
alert(e.message);
alert(e.stack); // non-standard property
}

The Error object also includes some non-standard properties. One commonly used property is stack, which provides a stack trace for the error. It's useful for debugging but should not be used on production websites.