Search justacoding.blog. [Enter] to search. Click anywhere to close.

October 6th, 2021

JavaScript Error Handling – A Brief Overview

In this article, we’ll go over how to handle errors in JavaScript. JavaScript error handling in general is fairly straightforward, it’s somewhat consistent with other languages in that regard.

There are some quirks to be aware of, however.

Let’s get into it!

What is a JavaScript error?

It’s easiest to think of JavaScript errors simply as objects that are thrown when something problematic happens.

The JavaScript error object

A JavaScript Error object is thrown whenever a runtime error occurs.

This error object contains useful information about the specific error that has occurred. The error object contains a name property as well as a message property. Both are useful with regards to both handling errors and debugging your code later on.

We can use this JavaScript error object to subsequently control the flow in our program, instead of just letting the program crash or fall over. Reacting to these errors in this way forms the very basis of our JavaScript error handling mechanism to begin with.

Types of JavaScript error

In JavaScript, there are different “types” of error. There’s also a generic error object, too.

The generic error object is defined by the Error constructor.

Some other, more specific error types commonly encountered out in the wild would be RangeErrors or TypeErrors.

We’ll get into the specifics of these error types later on, and look at how we can differentiate between these error types to react differently in each case.

For now, let’s look at how to go about generating our first JavaScript error.

The Throw statement

Before we can look at how to go about implementing our JavaScript error handling mechanism, we’ll need to have an error to work with!

Throwing a JavaScript error object

So let’s firstly go over throwing a JavaScript error.

The throw statement simply raises some sort of exception within our program. This exception is defined by us (the developer).

There are various error types we can choose to throw, each being applicable to a given scenario. There’s also the generic error type, which can be used when you don’t want to throw a specific type of error.

Here’s an example of a generic JavaScript error being thrown:

// no arguments are required
throw new Error()

// only supply the message
throw new Error("An error occurred")

// supply the message, file name and line number
throw new Error("An error occurred", "myfile.js", 108)

This is a “generic” error because it uses the generic JavaScript Error constructor, not one of the specific error types like RangeError or TypeError.

The specific error types work in the same way, however:

// RangeError type
new RangeError("A range error occurred")

// TypeError type
new TypeError("A type error occurred", "anotherfile.js", 5000)

So to raise one of the specific types of error, you can simply use the relevant constructor as per your requirement.

Which JavaScript error should I throw?

You may be wondering when you should use generic errors as opposed to the specific error types.

The answer to that is: it depends on your implementation. Soon, we’ll be catching these errors. If you need to segregate the “types” of JavaScript error object you are throwing so you can implement specific logic based on that further downstream, it’d be better to throw the specific error types.

For instance, if you need to react to or handle a RangeError in a different manner to how you’d react to a TypeError – you’ll want to throw the relevant error types instead of just generic errors for each case.

If you aren’t as concerned with the type of error that occurred and you want to handle the errors in a more generic way, then the default Error should work just fine.

I’m throwing an error, but it just breaks my app. So what now?

If you simply throw a JavaScript error object, you’ll notice it breaks the application.

That’s because throw is meant to be used in conjunction with try and catch. Throwing an error without catching it means the error isn’t handled at all. So your application simply errors in that scenario, which is not what we want to happen!

So, let’s take a look at how the try/catch block works and how it forms the basis of your JavaScript error handling.

The Try & Catch blocks

If you throw within a try block, you’ll have the option to catch the relevant JavaScript error object.

catch
You can control the flow of your program via catching errors

Simply put, this means the error won’t break your page, but you’ll have the ability to control what should happen next.

Take this example:

try {
    throw new Error("Something went wrong!")
} catch (e) {
    console.error("An error occurred: " + e.message)
}

We firstly throw the error, but then ensure that we catch it within the catch block.

Now the error has been handled (or caught). In this case, we’re simply just logging to the console, which is not especially useful. But you can do whatever is required by your application in the given scenario.

Accessing properties on the JavaScript error object

You’ll also notice that I’m accessing a message property on the error object.

We can access other properties on the JavaScript Error object in the same way, such as the name property and some other non-standard properties (fileName, lineNumber and so on).

You can read more about these JavaScript Error object properties over here.

Catching different JavaScript error types

Now, if we throw a specific error type instead of the generic error type as I’m doing above:

try {
    throw new TypeError("A TypeError occurred!")
} catch (e) {
    if (e instanceof TypeError) {
        console.error("A type error occurred: " + e.message)
    } else {
        console.error("An general error occurred: " + e.message)
    }
}

We can simply check the type of error, using instanceof as demonstrated above.

This means we can react to different JavaScript error objects in different ways, depending on their type!

An important point to note here is that the argument for the catch block is optional also. You don’t need to pass the error in to actually catch it; if you supply no argument your error will still be captured. You’ll just lack some of the information you may need in order to appropriately handle it.

Catching errors that you didn’t specifically throw

Of course, it’s possible to catch genuine errors that occur, also.

In the examples above, we are catching errors that we’ve specifically thrown to most simply demonstrate how try/catch works. We know at a certain point if there is some problem; so we can throw a JavaScript error object and catch it so that the problem is handled accordingly downstream.

This allows us to easily control the flow of our program.

In the real world, however, you’ll likely also be catching errors that you’ve not specifically thrown directly.

For instance:

try {
    JSON.parse("Not JSON")
} catch (e) {
    console.error("There was a problem with the supplied JSON: " + e.message)

    // the supplied JSON isn't in the correct format, we can't parse it
    // let's inform the user or something similar...
}

So let’s assume we do a lot of JSON parsing in our application. And for the sake of argument, let’s assume we’re parsing data from an unverified source — so we don’t even know if it’s legitimate or valid JSON to begin with.

In this case, JSON.parse will throw its own error should it encounter any issues.

Thankfully, we can simply wrap the parsing functionality in the try/catch, then react to this parsing error should it ever occur!

This type of JavaScript error handling is extremely common. Simply intercept any error that would otherwise cause your application to fall over, and handle it accordingly!

In case you’re wondering, the type of error JSON.parse would throw in this scenario is a SyntaxError.

Catching errors that are thrown deeper in the application

In our trivial JavaScript error handling examples — we are generating or throwing errors directly in the try/catch block.

It’s important to note that try/catch will still catch errors that are thrown by “nested” code. For example:

const someInternalParsingLogic = (json) => {
    JSON.parse(json)
}

const myParsingFunc = (json) => {
    someInternalParsingLogic(json)
}

try {
    myParsingFunc("Not JSON")
} catch (e) {
    console.error("There was a problem with the supplied JSON: " + e.message)
}

We can still catch an error that occurred several functions deep, try/catch doesn’t have to directly wrap the offending code!

The Finally statement

We can attach the finally statement to our try/catch block so that code is executed regardless of the result of the try/catch statement.

The code in the finally statement will always run:

try {
    throw new TypeError("A TypeError occurred!")
} catch (e) {
    if (e instanceof TypeError) {
        console.error("A type error occurred: " + e.message)
    } else {
        console.error("A general error occurred: " + e.message)
    }
} finally {
    console.log("try/catch completed")
}

It’s worth noting here that if you attempt to return something from either the try or the catch blocks, this won’t be the case.

Instead, we’ll always hit the finally block in this case (if it exists).

Creating a custom JavaScript error type

So we’ve covered the basics of how JavaScript errors work and how to handle them.

There may be cases, however, where it would be useful for you to define your own error type. This is an important consideration to make when thinking about how to implement your own JavaScript error handling system.

We’ve been using generic errors, RangeErrors and TypeErrors (along with the generic Error, also) in our examples. We can create and use custom error types in much the same way.

A custom JavaScript error class

Here’s a JavaScript error class that extends Error to provide some custom functionality:

// ES6 implementation
class MyCustomError extends Error {
  constructor(someProperty, ...params) {
    super(...params)

    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, MyCustomError)
    }

    this.name = "MyCustomError"
    this.someProperty = someProperty
  }
}

try {
  throw new MyCustomError("Hello")
} catch(e) {
  console.log(e.name + ": " + e.someProperty)
}

This is an ES6 implementation of a custom error type.

As you can see, it’s possible to define custom properties (for instance someProperty) on our custom JavaScript error object.

The usages of a custom error type are highly dependent on the specific requirements in your application, but in general utilizing these custom types is a good way to maintain clean and organized error-handling code.

Considerations when working with scheduled code

There are some nuances when working with JavaScript errors in general.

The most common of those being the behaviour of try/catch when paired with scheduled code.

Let’s look at an example.

Try/catch won’t work with scheduled code

In this example, the catch block will not be executed as we’re operating inside of setTimeout.

The code executed within the catch block is swallowed, and we’ll see no output with regards to that. This is definitely something to be conscious of when implementing your own JavaScript error handling!

try {
    setTimeout(() => {
        // throw an error after 1.5 secs
        throw new Error("Something went wrong!")
    }, 1500)
} catch (e) {
    // we won't see this...
    console.error("An error occurred: " + e.message)
}

To correct that, the try/catch block would need to be positioned within the function, instead. Like so:

setTimeout(() => {
    try {
        throw new Error("Something went wrong!")
    } catch (e) {
        console.error("An error occurred: " + e.message)
    }
}, 1500)

This would give us the behaviour we are looking for.

In conclusion

I hope you enjoyed reading the article, and I hope you’re now more familiar with the JavaScript error object as well as working with JavaScript error handling in general.

We’ve covered the basics of how what the JavaScript Error object is and how we can handle these errors when working with JavaScript. We’ve covered custom error types as well as some of the quirks to be aware of.

If you’re interested in more JavaScript-based articles, be sure to take a look at the JavaScript section of the blog.

For general advice with regards to improving your JavaScript skillset in general, I’d recommend this article I’ve recently written: How Do I Get Better At JavaScript? (18 Tips)

Thanks for reading!

Have any questions? Ping me over at @justacodingblog
← Back to blog