Error handling in JavaScript revolves around the idea of detecting, reporting, and recovering from errors. JavaScript has a built - in Error
object that can be used to represent and handle errors. There are several types of built - in error objects, such as SyntaxError
, ReferenceError
, TypeError
, etc.
The try...catch...finally
statement is used to catch and handle exceptions in JavaScript. The code inside the try
block is executed, and if an error occurs, the control is transferred to the catch
block. The finally
block is always executed, regardless of whether an error occurred or not.
try {
// Code that might throw an error
let result = 1 / 0; // This will throw a RangeError in some contexts
console.log(result);
} catch (error) {
// Handle the error
console.log('An error occurred:', error.message);
} finally {
// This block will always execute
console.log('This is the finally block');
}
The throw
statement is used to create and throw a custom error. You can throw any value, but it is recommended to throw an instance of the Error
object or one of its sub - classes.
function divide(a, b) {
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
}
try {
let result = divide(10, 0);
console.log(result);
} catch (error) {
console.log('Error:', error.message);
}
Promises are used for asynchronous operations in JavaScript. A promise can be either fulfilled or rejected. When a promise is rejected, it throws an error that can be caught using the .catch()
method.
function asyncOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Async operation failed'));
}, 1000);
});
}
asyncOperation()
.then(result => console.log(result))
.catch(error => console.log('Promise error:', error.message));
When using async/await
, errors can be handled using a try...catch
block.
async function main() {
try {
let response = await asyncOperation();
console.log(response);
} catch (error) {
console.log('Async/await error:', error.message);
}
}
main();
Logging errors is an important practice as it helps in debugging. You can use console.log
, console.error
, or more advanced logging libraries like winston
or bunyan
.
try {
let data = JSON.parse('invalid json');
} catch (error) {
console.error('Error parsing JSON:', error);
}
Graceful degradation means that the application continues to function, even if some features are not available due to an error. For example, if an API call fails, the application can display a default message instead of crashing.
async function fetchData() {
try {
let response = await fetch('https://example.com/api/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
let data = await response.json();
return data;
} catch (error) {
// Gracefully degrade
return { message: 'Data could not be fetched. Please try again later.' };
}
}
Error propagation is the process of allowing an error to be caught at a higher level in the call stack. Instead of handling the error immediately, you can let it bubble up to a more appropriate place for handling.
function innerFunction() {
throw new Error('Error in inner function');
}
function outerFunction() {
try {
innerFunction();
} catch (error) {
// Optionally, add some additional context
throw new Error('Error in outer function due to inner function', { cause: error });
}
}
try {
outerFunction();
} catch (error) {
console.log('Caught error:', error.message);
}
Instead of using a generic Error
object, use more specific error types like SyntaxError
, TypeError
, or create custom error classes. This makes it easier to handle different types of errors.
class CustomError extends Error {
constructor(message) {
super(message);
this.name = 'CustomError';
}
}
function validateInput(input) {
if (typeof input!== 'number') {
throw new CustomError('Input must be a number');
}
return input;
}
try {
validateInput('abc');
} catch (error) {
if (error instanceof CustomError) {
console.log('Custom error:', error.message);
} else {
console.log('Other error:', error.message);
}
}
Silent failures occur when an error is not reported or handled properly. Always make sure that errors are logged or reported to the user.
function calculateSum(arr) {
if (!Array.isArray(arr)) {
// Avoid silent failure
throw new TypeError('Input must be an array');
}
return arr.reduce((sum, num) => sum + num, 0);
}
try {
calculateSum('not an array');
} catch (error) {
console.log('Error:', error.message);
}
The Error
object has properties like message
and stack
. Use these properties to get more information about the error.
try {
let result = JSON.parse('invalid json');
} catch (error) {
console.log('Error message:', error.message);
console.log('Error stack:', error.stack);
}
Error handling is an essential part of building reliable JavaScript applications. By understanding the fundamental concepts, using the right techniques like try...catch...finally
, throw
, handling promise rejections, and following common and best practices, you can create robust applications that can gracefully handle errors. Remember to be specific with error types, avoid silent failures, and use error objects effectively to make debugging easier.