JavaScript Errors

In JavaScript, we have a native error constructor function which we can create instances of it by using the “new” keyword”. the instance itself doesn’t do anything but by using the “throw” keyword with the error instance, interesting things can happen.

When we use the “throw” keyword, the script that currently running stops executing unless we handle the “throw” somehow. so if the we use throw inside a function, the execution of the current function will stop and JavaScript will move to the next part of the call stack.


console.log(Error) // constructor function
console.log(new Error('Something Went Wrong Here')); // Error: Something Went Wrong Here
console.log(throw new Error())  // we get a proper error

The Error instance have 3 built in properties: name, message and the stack (stack trace) which is a string that shows where the error happen (shows where it happen in the call stack).

JavaScript has many built in constructors for errors:


new Error());  // generic error
new SyntaxError());  // syntax error
new ReferenceError();  // reference error


new Error('Something Went Wrong Here'));
console.log(Error) // constructor function
console.log(new Error('Something Went Wrong Here')); // Error: Something Went Wrong Here
console.log(throw new Error())  // we get a proper error

The JavaScript Runtime (the browser) handles the errors if we are not handle it ourselves, it will run the “onError()” (runtime catch) if it can find a “catch” in the stack.so to handle errors ourselves we have to add a “catch” statement.

We have 2 ways that we can catch errors: the try and catch block and the catch method.

Synchronous Error Handling

With the try catch block we can catch any type of synchronise errors. the “catch” block excepts an error parameter which is what gets thrown from the “try” block. so in the “try” block we write the code that supposed to be run and in the “catch” block we handle any error that happen in the code that sits in the “try” block


function doSomething() {
  try {
    // notice the error in console.log
    cosole.log('Hi');
  } catch (error) {
    console.log('we have an error, error');
  }
}

doSomething();  // we have an error referencError: cosole is not defined (and stack trace string)

Lets see another example:


function doSomething() {
  try {
    console.log('Hi');  // Hi will be printed
    throw new Error('Fail');
    console.log('Hi Again);  // will not be printed
  } catch (error) {
    console.log('we have an error, error');
  }
}

doSomething();  // we have an error Error: fail (and stack trace string)

There is another keyword name “finally” which will run the code inside the block no matter if the code succedded or failed:


function doSomething() {
  try {
    throw new Error('Fail');
    console.log('Hi);  // will not be printed
  } catch (error) {
    console.log(error.message);
  } finally {
    console.log('This text will be printed no matter if the code above pass or failed');  // will be printed
  }
}

doSomething();

Asynchronous Error Handling

When working with asynchronice code, we cant use the try-catch block. because when there is an error, the Javascript engine will look for the catch block in the call stack and when we work with asynchronice codecode, the call stack will be empty by the time we get the callback from the browser (JavaScript Runtime).

To handle an asychronise errors we need to use the catch method. as you know, promises are asynchronise. lets see an example how to handle error with promises:


Promise.resolve('async code')
  .then(response => {
    throw new Error('an error');
    return response;  // will not return since we throw an error above this line
  })
  .then(response => {
    console .log(response); // will not be printed since we throw an error
  })
  .catch(err => {
    console.log(err);  // will be printed
  })

doSomething();

by the way, if we will chain another then after the catch , it will run since it is below the catch. the catch method handle errors that happen above it and not below it.

The async and await keywords are promises under the hood but because they look synchronous, we can use “try” and “catch” blocks with them and that’s how we catch errors with them.


(async function() {
  try {
    await Promise.reject('failed');
  } catch (err) {
    console.log(err);  // "failed" will be printed
  }
  console.log('will I be printed?');  // "will I be printed?" will be printed since it is after the try and catch block
})()

Extending Errors

The Error is a constructor function so we can extend it.

Let’s see an example:


class authenticationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'authenticationError';
    this.color = orange;
  }
}

throw new authenticationError('authentication failed');  // authenticationError: authentication failed