Error Handling in JavaScript along with Promises
Learn about handling exceptions during the runtime along with its potential danger when working with Asynchronous code

Errors are like shooting yourself in the foot without realizing it. Exception handling, also known as error handling is just about handling a potential breakage of your code.
Try-Catch block
Itβs just saying try this code and if it throws an error then catch it.
try {
console.log("π€‘ I am perfect");
} catch (e) {
console.log(e);
}
console.log("β
Everything worked out fine");
// Output:
// π€‘ I am perfect
// β
Everything worked out fine
Here, inside the try block, we put code that has the potential of throwing an error. If it doesnβt throw an error then the catch block will not be executed and we move to the next piece of code. But if it throws an error then we will jump directly to the catch block executing the code inside it and then moving to the next piece of code.
try {
throw new Error("π« You have been shot!");
} catch (errorInfo) {
console.log(errorInfo);
console.log(errorInfo.message);
}
console.log("β
Everything worked out fine");
The output will be:

Here, the catch block handles the exception thrown by the throw keyword. The errorInfo variable for the catch block holds the caught exception. We will learn more about throw and Error later in the post.
Using finally
You may want to run some code no matter if thereβs an exception raised OR not. In this case, you can use the finally block with try-catch.
try {
throw new Error("π€ I will take care of Humans");
} catch (err) {
console.log(err.message);
} finally {
console.log("π€ I will be back");
}
// Output:
// π€ I will take care of Humans
// π€ I will be back

Raising exceptions
Sometimes you want to raise an exception when conditions are not met. You can do that in JavaScript using the throw keyword.
The syntax to do so is:
throw expression;
The expression can be any valid expression in JavaScript. Some of the examples are:

Using throw inside a try-catch block
In the following snippet, there is a function named raiseAnError which takes an expression as an argument. This function raises an exception using the throw keyword and handles it using try-catch and then in the catch block, it prints the err.
const raiseAnError = (expression) => {
try {
throw expression; // Raising an error
} catch (err) {
console.log(err); // Logging the error
}
};
raiseAnError("π« stop now!"); // π« stop now!
raiseAnError(0); // 0
raiseAnError(true); // true
raiseAnError(false); // false
raiseAnError([]); // []
raiseAnError(2 + 2); // 4
raiseAnError(5 > 10); // false
Throwing errors using a class
You can use throw and a class to raise errors. This class can be a custom one OR the pre-built JavaScript error classes like Error, SyntaxError, ReferenceError, TypeError, etc⦠Learn more on types of native errors in this post.
Using pre-built classes
The syntax for this is shown below where the message is the attribute that will be displayed when the error is thrown.
throw new Error(message);
throw new SyntaxError(message);
throw new TypeError(message);
// and soo...

You can handle these errors in your code by using the try-catch.
try {
throw new TypeError("π Something went wrong");
} catch (err) {
console.log(err.name);
console.log(err.message);
}
// Output:
// TypeError ππ½ name of the class
// π Something went wrong ππ½ message attribute
You can learn more about using throw <some-object> v/s throw Error in this Stackoverflow post.
Custom classes
Using custom classes allows us to add additional information and functionalities for raising errors as per our needs. An example of this is the CustomError class:
class CustomError {
constructor(message, tags, priority) {
this.message = message;
this.tags = tags;
this.priority = priority;
}
priorityInfo() {
return `Priority to solve this error is ${this.priority}`;
}
}
try {
throw new CustomError("π€‘ Custom error", ["error", "custom"], "π₯ high");
} catch (err) {
console.log(err.message);
console.log(err.tags);
console.log(err.priority);
console.log(err.priorityInfo());
}
// Output:
// π€‘ Custom error
// [ 'error', 'custom' ]
// π₯ high
// Priority to solve this error is π₯ high

Handling multiple types of errors
You can not use multiple catch blocks to handle multiple types of errors. There is only one try, catch, and finally block respectively. So to handle multiple types of errors youβve to use if[...else if]...else inside the catch block.
try {
throw SyntaxError("Syntax error");
} catch (err) {
if (err instanceof TypeError) {
console.log("π TypeError");
} else if (err instanceof SyntaxError) {
console.log("π SyntaxError");
} else {
console.log("π» Some unknown error");
}
}
// Output:
// π SyntaxError

Error handling when working with Promises
The try-catch is by default synchronous. That means that if an asynchronous function throws an error in a synchronous try-catch block, no error throws.
const asyncCode = async () => {
throw new Error("π€‘ I will show up no matter what!");
};
try {
asyncCode();
} catch (err) {
console.log(err.message);
}
The output is:

So while working with asynchronous code either use the .then and .catch where you can handle the errors inside the .catch OR await the async function inside a try-catch block where the catch block will handle the error.
const asyncCode = async () => {
throw new Error("π€‘ I will show up no matter what!");
};
// Using .catch() to handle the error
asyncCode().catch((err) => console.log(err.message));
// Output: π€‘ I will show up no matter what!
// Using try/catch to handle the error (with Currying)
(async () => {
try { await asyncCode(); }
catch (err) { console.log(err.message); }
})();
// Output: π€‘ I will show up no matter what!
Pro-tip for handling errors with async-await
Handling errors in async-await using try-catch can become quite repetitive. Like the one shown below
const asyncCode = async () => {
throw new Error("π€‘ I will show up no matter what!");
};
// Using Currying to execute this code
(async () => {
try {
await asyncCode();
} catch (error) {
console.log(error.message);
}
try {
await asyncCode();
} catch (error) {
console.log(error.message);
}
try {
await asyncCode();
} catch (error) {
console.log(error.message);
}
})();
// Output:
// π€‘ I will show up no matter what!
// π€‘ I will show up no matter what!
// π€‘ I will show up no matter what!
As you can see it is just too much code. The following snippet is a DRY implementation where runAsync handles errors using try-catch and returns an array where array[0] is the result and array[1] is the error.
const asyncCode = async () => {
throw new Error("π€‘ I will show up no matter what!");
};
// This function will handle errors and will return an array
// This array's first value is the `result` & second value is the `error`
const runAsync = async (promise) => {
try {
const result = await promise();
return [result, null];
} catch (error) {
return [null, error];
}
};
(async () => {
const [result, error] = await runAsync(asyncCode);
console.log(result, error.message);
const [result1, error1] = await runAsync(asyncCode);
console.log(result1, error1.message);
const [result2, error2] = await runAsync(asyncCode);
console.log(result2, error2.message);
})();
// Output:
// null π€‘ I will show up no matter what!
// null π€‘ I will show up no matter what!
// null π€‘ I will show up no matter what!
Logging Errors
You can use custom loggers like Winston, etc... to log errors and save them in a log file. This will help you learn more about your errors. You should also explore remote exception handling. Learn more about it in this post.
You can use the console of JavaScript to do assertion checks and log errors using console.assertion and console.error.
console.assertionwill take the first argument as a condition and if then fails then it will log an error and if it succeeds then it will log nothingconsole.errorwill log the message passed to as an error

Conclusion
Iβve covered how to raise, handle errors, and work with errors in asynchronous code. If you liked this post then do π like and give your π feedback.





