DEV Community

Cover image for Handling exceptions in JavaScript : Key points ⭐️
_CODE
_CODE

Posted on

Handling exceptions in JavaScript : Key points ⭐️

Hello, everybody! πŸ‘‹πŸΌ

In today's article we'll be talking about exceptions in JavaScript: what they are, different types, declaration and how to handle and deal with them appropriately.

First things first: Main concepts 🧐

Let's first have a look at the main concepts of exception handling to have a clear and concise idea of what they exactly mean in programming.

What is an exception? 🚫

An exception is an anomalous condition that breaks the regular execution of the code.

What does handle an exception mean? πŸ‘‰πŸΌ

Exception handling is the method by which an exception is caught and managed.

Then, how can we handle exceptions in JavaScript? πŸ•Ή

To handle exceptions we'll use the try...catch statement provided by JavaScript.

As easy as that 😊

The try...catch statement 🀠

The try...catch statement is a block that tries to perform one or several actions that can lead to error conditions and defines the instructions to handle such errors appropriately in case they happen to occur.

Let's see the structure for this statement:

try{
/*
 * Action(s) to be performed that can lead
 * to an anomalous condition.
 */
}
catch(e){
/*
 * Error / exception handling.
 */
}
finally{
/*
 * Instruction(s) to be executed after
 * the try...catch statement ends.
 */
}
Enter fullscreen mode Exit fullscreen mode

Things to consider:

  • Curly braces must be used anytime, even for one line statements within the blocks.
  • A catch or a finally block must be defined as part of the try...catch statement, but neither of them are mandatory separately.
  • The previous point results in the following types of try...catch statements:
    • try...catch
    • try...finally
    • try...catch...finally

The throw statement 🎳

The throw statement, as its own name indicates, throws an exception when something wrong occurs within the function or block it is declared.

When a throw statement is found, the execution of the current function stops and the control is shifted to the catch block.

A very simple example:

try{
  throw new Error('Something went wrong!');
}
catch(e){
  console.error(e);
}
/*
 * Output:
 * Error: Something went wrong!
 */
Enter fullscreen mode Exit fullscreen mode

A throw statement is usually called within the try block, but that doesn't mean that can't be called within a catch block (to rethrow an exception once it has been caught) or a finally block (to throw an exception no matter which previous block has been executed).

▫️ Rethrowing an exception in the catch block:

try{
  try{
    throw new Error('πŸ™„');
  }
  catch(e){
    console.error(e);
    throw e;
  }
}
catch(e){
  console.error(e);
}
/*
 * Output:
 * Error: πŸ™„
 * Error: πŸ™„
 */
Enter fullscreen mode Exit fullscreen mode

▫️ Throwing an exception in the finally block:

try{
  try{
    throw new Error('Error - try block');
  }
  catch(e){
    console.error(e);
    throw new Error('Error - catch block'); //Note that this exception is never caught
  }
  finally{
    throw new Error('Error - finally block');
  }
}
catch(e){
  console.error(e);
}
/*
 * Output:
 * Error: Error - try block
 * Error: Error - finally block
 */
Enter fullscreen mode Exit fullscreen mode

User-defined exceptions πŸ‘¨πŸ»β€πŸ’»πŸ‘©πŸ»β€πŸ’»

A user-defined exception is a custom exception that can contain any valid expression.

The following statements are all correct:

throw 'Error!';
throw 404;
throw false;
Enter fullscreen mode Exit fullscreen mode

Let's see an example where an object is thrown.

Note that object properties become accessible once the exception is caught, through the error itself:

const UserException = {
  name: 'UserException',
  message: 'There was an error πŸ™ƒ'
}

try{
  throw UserException;
}
catch(e){
  console.error(`${e.name}: ${e.message}`);
}
/*
 * Output:
 * UserException: There was an error πŸ™ƒ
 */
Enter fullscreen mode Exit fullscreen mode

JavaScript built-in errors can also be used as objects for user-defined exceptions and be thrown accordingly.

Take a look at the following example, where a TypeError error is thrown if the type of the required value is not String.

const TypeException = function(){
  const msg = 'There was an error regarding the data type.';
  this.error = new TypeError(msg);
}

const isString = value => typeof value === 'string';

const city = {};

const setCity = cityName => {
  if(isString(cityName)) city.name = cityName;
  else throw new TypeException();
}

try{
  setCity(28);
  console.log('--- City name has been set correctly.---')
}
catch(e){
  console.error(e.error);
}
finally{
  console.log('--- The execution has finished ---');
}
/*
 * Output: 
 * TypeError: There was an error regarding the data type.
 * --- The execution has finished. ---
 */
Enter fullscreen mode Exit fullscreen mode

Conditional catch blocks πŸš€

There may be occasions where the try...catch block throws different types of exceptions.

In order to handle each of them in a proper way, we can use if...else statements within the catch block.

let month;
const setMonth = monthValue => {
  if(typeof monthValue !== 'number') throw new TypeError(monthValue);
  if(monthValue <= 0 || monthValue > 12) throw new RangeError(monthValue);
  month = monthValue;
}

try{
  setMonth(-5);
  console.log(`-- Month ${month} has been set correctly ---`);
}
catch(e){
  const errorInfo = 'Please enter a number [1-12]';
  if(e instanceof TypeError)
    console.error(`Wrong data type: ${e.message}. ${errorInfo}.`);
  if(e instanceof RangeError)
    console.error(`Out of range: ${e.message}. ${errorInfo}.`);
}
/*
 * Output: 
 * Out of range: -5. Please enter a number [1-12].
 */
Enter fullscreen mode Exit fullscreen mode

We could also use a switch statement to handle multiple exceptions, being both examples equivalent:

...
try{
  setMonth(-5);
  console.log(`-- Month ${month} has been set correctly ---`);
}
catch(e){
  const errorInfo = 'Please enter a number [1-12]';
  switch(e.name){
    case 'TypeError':
      console.error(`Wrong data type: ${e.message}. ${errorInfo}.`);
    case 'RangeError':
        console.error(`Out of range: ${e.message}. ${errorInfo}.`);
  }
}
/*
 * Output: 
 * Out of range: -5. Please enter a number [1-12].
 */
Enter fullscreen mode Exit fullscreen mode

Summary: key points πŸ’«

  • Exceptions are error conditions that break the normal flow of the code execution.
  • An exception can be any kind of expression: a number, a string, an object...
  • Exceptions should be thrown and handled appropriately in order to not crash the app and let the user know that something didn't go as expected.
  • JavaScript built-in errors can be used as throwable objects for user-defined exceptions.
  • Multiple exceptions can be handled within the same catch block.

⚑️ Related post on Instagram:


And that's all for today! πŸ˜‡

A big thanks for reading πŸ€— and don't hesitate to reach out to me if you have any questions or doubts about today's article.

Rachel Green from Friends TV Show behind a desk saying "Ask me anything"

I hope you found this article useful and I see you all in the next πŸ‘‹πŸΌ


πŸŽ‰ Don't forget to follow @underscorecode on Instagram and Twitter for more daily webdev content: info, challenges, quizzes & more πŸ’œ


And last but not least... A quick friendly reminder before we go 😊

We all know there are million ways to get things done when it comes to programming and development, and we're here to help and learn, so, if you know another possible way to do what others are sharing (not better, not worse, just different), feel free to share it if you feel like it, but, please, always be kind and respectful with the author and the rest of the community. Thank you and happy coding!

Discussion (1)

Collapse
lukeshiru profile image
LUKESHIRU • Edited

2 tips:

  • You can extend the Error class with your custom errors, so instead of using a plain object for UserException, you can do the following:
class UserException extends Error {
    name = "UserException";
    message = "There was an error πŸ™ƒ";
}
// ...
throw new UserException();
Enter fullscreen mode Exit fullscreen mode
  • You can replace try/catch with promises if you prefer that style. Using your examples to illustrate (except the side effect because those are EVILβ„’):
/** @param {number} monthValue */
const setMonth = month => {
    const errorMessage = "Month has to be a number between 1 and 12";

    return typeof month !== "number"
        ? Promise.reject(new TypeError(errorMessage))
        : month < 1 || month > 12
        ? Promise.reject(new RangeError(errorMessage))
        : Promise.resolve(month);
};

setMonth(-5)
    .then(month => console.log(`-- Month ${month} has been set correctly ---`))
    .catch(console.error);
Enter fullscreen mode Exit fullscreen mode

And the good thing about this approach, is that you can still use try/catch with async/await:

try {
    const month = await setMonth(-5);
    console.log(`-- Month ${month} has been set correctly ---`);
} catch (error) {
    console.error(error);
}
Enter fullscreen mode Exit fullscreen mode

I personally haven’t written a try/catch statement in JavaScript in a while because Promises are way more flexible from my point of view.

Cheers!