What is an Error?
An error is an illegal operation performed by the user which results in the abnormal working of the program.
Types of Errors: Errors can be broadly categorized as
- Operational Errors: Errors that happen while a program is undertaking a task. For instance, network failure would be an operational error
- Developer Errors: This type of error occurs when a developer has made a mistake. The main example of this is invalid input. In these cases, the program should not attempt to continue running and should instead crash with a helpful description so that the developer can address their mistake
Throwing Errors: Typically an input error can be dealt with the use of the throw keyword.
As we can observe that when the program is run, we get an Error stating that the amount must be a number.
Thus we can restrict the types of inputs that can be accepted by our program and then stop the execution of the program so that no unexpected or no wrong output is generated.
1. Reference Error: When we use a variable that is not defined or which is yet to be defined the Error of type ReferenceError is thrown
node -p "thisVariableIsNotDefined"
This is because there is no variable named thisVariableIsNotDefined, and thus, a ReferenceError is thrown.
throw new EvalError('message', 'file_where_error_occurs', 'line_number')
We declare two variables namely a and b and initialize them with some numeric value. Now we check if variable b is equal to 0 and if so throw a new EvalError and then handle it in the try/catch block, where we print the error type and the error message.
To understand how eval() consider the example below:
As you can see that an operator, alongside two operands, is required by the eval() function.
Let us now see what happens if you do not pass an operator in the eval() function:
We can observe that the SyntaxError occurs as the eval() function requires an operator along with two operands and thus it throws an error; as there is no operand to be found.
4. RangeError: A RangeError is thrown when an out-of-bounds value is encountered. For example, in a bank application where the balance of the account cannot be negative.
The above block of code is very easy to understand, what it does is that it generates a random 3-digit number, but take a look at the if condition. We have specified that only values from 0 to 99 are allowed, now since the random() function generates only 3 digit number, the range error occurs.
5. TypeError: The TypeError object represents an error when an operation could not be performed, typically (but not exclusively) when a value is not of the expected type. Consider the following example where we write a function to add two numbers and expect that both the arguments passed to the function are of type number, if not then throw a TypeError:
The above block of code upon execution will result in TypeError as one of the arguments passed is of type string, which is obviously not a number.
6. URIError: URIError occurs if the encoding or decoding of URI is unsuccessful. This error is thrown as somewhere in the code, the URI encoding or decoding is unsuccessful.
Consider the lines of code in the following snippets:
First is the example of valid URI Encoding:
Here is another example we pass an invalid encoding string which results in URIError, which outputs the string ‘URI Malformed’:
We can also verify the error object and check if the error object is an instance of a particular class.
Consider the examples below:
First, we check if the Error is an instance of the Error Class that returns true, then we check if SyntaxError is an instance of the Error Class that returns true because the SyntaxError is inherited from the Error class. Lastly, we check if SyntaxError is an instance of ReferenceError Class which returns false because the SyntaxError is inherited from the Error class and not ReferenceError Class.
Custom Errors: Let’s update the doTask function to throw a custom error when the entered amount is an odd number, here is the implementation of the doTask function
Upon running the above code snippet this is what we get in the output window:
This is one way of defining a custom error, another method to create a custom error by inheriting from the Error Class.
Here is an implementation of a custom Error Class.
Upon executing the above snippet code we get something like this in the output window-
We can also add the code property to our custom error class; We can make changes to OddError Class and do something like this:
When we use the above-updated custom error class we also get the error code in the output when the Error occurs –
Error Handling Using Try/Catch Block: When an error is thrown in a normal synchronous function it can be handled with a try/catch block.
Using the same code from the previous section, we’ll wrap the doTask(3) function call with a try/catch block:
Executing this updated code will result in the following:
In this case, we controlled how the error was output to the terminal but with this pattern, we can also apply any error handling measure as the scenario requires.
Let’s update the argument passed to doTask to a valid input:
This will result in the following output:
Rejection: The throw in a synchronous context is known as an exception. When a promise rejects, it’s representing an asynchronous error. One way to think about exceptions and rejections is that exceptions are synchronous errors and rejections are asynchronous errors.
Let’s imagine that doTask has some asynchronous work to do, so we can use a callback-based API or we can use a promise-based API (even async/await is promise-based).
Let’s convert doTask to return a promise that resolves to a value or rejects if there’s an error:
But wait the rejection is unhandled because promises must use the try/catch method to catch rejections and so far we haven’t attached a catch handler. Let’s modify the doTask call to the following:
When we call the doTask function with a valid value it will resolve and print the result.
Asynchronous Try/Catch: The async/await syntax supports try/catch of rejections. In other words, we can use try/catch on asynchronous promise-based APIs instead of using then and catch handler as in the next section, let’s create an async function named run and reintroduce the same try/catch pattern that was used when calling the synchronous form of doTask:
The only difference, other than wrapping the try/catch in an async function, is that we await doTask(3) so that the async function can handle the promise automatically. Since 3 is an odd number, the promise returned from doTask will call reject with our custom OddError and the catch block will identify the code property and then output – cannot be odd:
This function works the same as the promise-based approach, the only difference is that it is implemented using the Try/Catch block asynchronously.
It is always good to handle the Errors at the top-most level of your code, for example- app.js in NodeJS is the entry point of the application, where all the errors should be handled.
An error can be propagated to the top-most level by re-throwing the error, consider the snippet below
Consider the parent_function as the entry point of the program and also observe that we handled the errors that occur in child_function inside the parent_function.
Perhaps this is confusing but, you will understand what the above snippet does once you take a look at the output:
Please Login to comment...