In JavaScript, errors are only detected during runtime. Therefore, it is impossible to get compile-time errors when building JavaScript applications.
There are several reasons an app might encounter a JavaScript exception: invalid input, server error, syntax errors, or errors in the application logic.
In this article, we will dig into exception handling in JavaScript so that when your app encounters errors, it affects users as little as possible. Exception handling allows us to write custom behavior for different errors caused by application users, the server, and many other causes of these errors.
What is exception handling?
Applications occasionally run into problems like syntax errors. Operations such as dividing a non-zero number by zero, calling a method with incorrect arguments, or failing to read a file could give a JavaScript exception in our code.
JavaScript exception handling is the procedure/method used to handle the abnormal behavior of our JavaScript applications. It is the method used to handle the possibility of failure in applications as gracefully as possible.
With proper exception handling and good logging, we can handle abnormal conditions or crashes and help the user take necessary actions with a proper exception handling error messages.
Why is exception handling in JavaScript important?
When application code generates errors and raises exceptions, it often doesn’t make sense to continue with the remaining code. Instead, applications should try to recover from the error by using exception handling.
Without properly handling JS exceptions, they may crash the application and frustrate a user. Noticing and handling errors or possible failures of an application, often called "catching the exception", saves programs from cascading the failure up the call stack.
If an unexpected response is given method and then processed by the caller, the user gets incorrect information and a bad impression of the application. A well-written application should have a good exception handling mechanism and approach to various kinds of errors that are likely to happen within the application during usage.
With exception handling, we can manage these problems and prevent the application from crashing. By using exception handling, we can control the flow of an application.
How to handle exceptions in JavaScript
There are many ways to handle exceptions in JavaScript. JavaScript provides a mechanism to handle errors using the try-catch-finally block, similar to languages like Java and C#.
The mechanism can be used in a few different ways:
- throw statements
- try-catch
- try-catch-finally
Try blocks
Executable code that you know may raise exceptions or cause errors should be placed in a try
block.
When an exception is thrown in the try
block, it passes the error to the catch
block, and the necessary actions can be taken in the catch
block. If the necessary actions are not taken by the catch
block, the application may behave abnormally, and a crash is likely to occur.
Catch blocks
The code that should be executed when JavaScript exceptions happen in the try block is written in the catch
block. The exception handling code is usually written in the catch
block.
The catch
block can contain either the user-defined exception handler or the built-in error handler in JavaScript.
Note that the catch
block only executes when there is an error from the try
block. There can also be more than one catch
block to handle multiple exceptions.
Finally blocks
The finally
block will execute regardless of whether there is a syntax error from the try
block.
Try-catch statements
Try-catch statements are not only common in JavaScript but also widely used in other programming languages. They are used to handle exceptions that arise in the code.
JavaScript uses try-catch statements to trap runtime errors and handle them properly.
Try blocks contain code that might throw runtime errors, and a catch statement contains code to handle the exception if an error occurred. When a try block encounters errors, the catch block is triggered. Otherwise, execution continues after each try-catch statement. Here is an example:
try {
thisfunctiondoesnotexit()
}
catch(e) {
console.log(e);
}
In the code above, we attempt to use a function that does not exist in the application. The try block throws an error to the catch block, which logs it to the console.
JavaScript try-catch-finally statements
A try-catch
statement can also be written with an added finally
block. The code in the finally
block executes, regardless of whether an error occurs in the try statement.
The following is an example of how you can use try-catch-finally
to handle errors in JavaScript:
try {
thisfunctiondoesnotexit();
}
catch(e) {
console.log(e);
}
finally {
console.log("Code executed successfully");
}
In this example, the finally
block will run regardless of whether the try
block throws an error.
Using throw
statements allows for custom errors to be thrown into the catch block, which is handled based on the code in the catch
block.
Let's take a look at some more complicated JavaScript error handling examples. We will create a simple calculator that accepts user input in JavaScript and make use of exception handling and promises to generate custom error messages.
Run the JavaScript code below in your browser, and we'll walk through how it works.
const throwExceptions = () => {
let result
let first = prompt('Enter first number: ')
let second = prompt('Enter second number: ')
try {
if (first == '' || second == '') throw 'Number cannot be empty'
result = first / second
} catch (err) {
console.log(err)
} finally {
console.log(`The division of ${first} & ${second} is ${result}`)
}
}
throwExceptions()
Here, we have created a function that handles exceptions it might encounter.
In this example, we prompt the user to input two numbers. These two numbers will then be divided by each other, with the first number being the numerator and the second being the denominator.
Then we use the JavaScript if
and throw
statement to test whether the input given by the user is valid (i.e., whether it is empty).
In our code, we throw a custom error using the throw
statement. We can also do some other checks on the user input to check for validity and send the JavaScript errors to the catch
block if there are any. The catch
block catches errors and displays the message defined in throw
statements.
This allows for control and better flow handling of the application by the developer and also helps the user to know what went wrong and why the application does not give the expected result.
The finally
block executes regardless of whether an error is thrown and is used to display the result of the operation, even when there is an error.
In addition to errors defined by the throw
statement, other errors can also be caught and displayed in the catch block. The throw
statement only allows for flexibility and custom error definition.
Using the try-catch-finally
statement in JavaScript is a good example of how to handle exceptions and errors. However, there is more.
Handling errors with JavaScript promises
We can also catch errors using JavaScript promises. We will build the same program using JavaScript promises. Run the JavaScript code below in your browser, and let's take a closer look.
const throwExceptions = (first, second) => {
return promise = new Promise((resolve, reject) => {
if (first == '' || second == '') reject ('Number cannot be empty')
resolve(first / second)
})
}
let first = prompt('Enter first number: ')
let second = prompt('Enter second number: ')
throwExceptions(first, second)
.then(result => console.log(result))
The code above helps us handle our simple calculator script using JavaScript promises. While the implementation differs, the functionality of this example is the same as the previous one. Only the implementation differs. We have only used promises to more effectively handle our errors.
Promises are a great way to handle errors in asynchronous code. They provide better ways to handle asynchronous operations than callbacks and events.
We created and returned a new promise object in our function: throwExceptions
. Our new promise tests for the input and rejects it if it is invalid. The operation will be processed, and the result will be displayed if the input is valid.
We can do more checking and get more detailed information about errors when they occur with Honeybadger, an application monitoring tool.
How to handle exceptions with Honeybadger
Honeybadger provides developers with a tools to better understand and fix errors. With Honeybadger, developers and businesses can track and manage application errors, keeping users happy.
Let's walk through the steps you need to get started with Honeybadger and how to monitor errors in your application with Honeybadger.
In this application, we will try to access a function that does not exist. This will raise an error. We will then use Honeybadger to monitor and get more detailed information about this error.
Step 1 - Create a Node project
Start by creating a Node project. Create a folder and give it a meaningful name.
mkdir test-app && cd test-app && npm init
Then, install Express:
npm install express
Step 2 - Write a simple hello world app
Create an index.js
file in the root of the application and put in the following code:
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Step 3 - Sign up for Honeybadger
Sign up for a Honeybadger Free trial.
The Honeybadger homepage
Step 4 - Create a new Honeybadger project
Click on ‘add project’ and give your project a name. Next, select Node.js, and you'll see instructions for installing Honeybadger:
Creating a new Honeybadger project
Step 5 - Install and configure Honeybadger
First, install Honeybadger as an NPM dependency:
npm add @honeybadger-io/js --save
Finally, we can configure Honeybadger with the API key we were given when creating the new project, then throw and catch an exception to demonstrate reporting to Honeybadger. Update index.js
to this:
const express = require('express')
const app = express()
const port = 3000
// Configure honeybadger
const Honeybadger = require("@honeybadger-io/js");
Honeybadger.configure({
apiKey: "hbp_1bVSXwW2zJwZio8NV6IDeCNpSbY1yk3889mg",
environment: "production"
});
app.get('/', (req, res) => {
try {
thisfunctiondoesnotexit()
} catch(e) {
Honeybadger.notify(e);
}
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
In the program above, we try to access a function that does not exist. This code produces an error when it runs. Let’s take a look at how Honeybadger helps us manage this error from our Honeybadger account.
Run the project locally with:
node index.js
Then, trigger the code by visiting localhost:3000
in your browser.
Navigate to your Honeybadger account and open up the project we created earlier. Click on the error tab and see which error is displayed. To get more information about this error, click on the error. This will display more detailed information about the error in your code, as shown in the following image:
The Honeybadger error page
If you have successfully configured your application with Honeybadger, you should get a screen that looks like this. This screen shows the ReferenceError
in your code.
This page displays all relevant information about the error in your code, such as the error message, at what line the error occurred, and other relevant details. Honeybadger is a great tool for developers who are willing to satisfy their customers and minimize errors and bugs in their applications.
Try out the simple calculator we have built in this article and see how Honeybadger helps you track these errors conveniently.
Building reliable JavaScript applications
In JavaScript development, errors are bound to happen. Errors in application code can be tedious to find and can be expensive when they occur and crash applications. With techniques for exception handling in JavaScript and even monitoring tools like Honeybadger, you can ship with confidence.
Effective error handling isn't just about preventing crashes, but building a better user experience and ultimately shipping more reliable applications. As you continue your JavaScript journey, make exception handling an integral part of your coding practice.