Without errors, we can't imagine developers' lives. Whenever we create or build an application we we have to face several errors. And this is very annoying. In this article, we will focus on how we can manage or avoid errors effectively.
Without error handling, we can't properly read the errors. Because the error language is very weird. So for better understanding, we should handle the errors.
Types of Error
Syntax Errors: These are errors that occur when you have a mistake in your code's syntax. Common examples include missing semicolons, parentheses, or curly braces.
Runtime Errors: Runtime errors occur when your code is syntactically correct, but there's an issue during execution. Common examples include trying to access an undefined variable or calling a function that doesn't exist.
Logical Errors: These are the trickiest errors to spot because they don't result in immediate crashes or error messages. Instead, your program may produce unexpected or incorrect results due to a flaw in your logic.
Asynchronous Errors: Node.js is known for its asynchronous, non-blocking I/O operations. Errors that occur in asynchronous code can be challenging to handle. Common examples include callback errors, promise rejections, and unhandled promise rejections.
System Errors: These errors are related to the underlying system or environment in which your Node.js application is running. Examples include running out of memory, file system errors, and network errors.
Custom Errors: You can create custom error objects in Node.js to represent specific error conditions in your application. This can make error handling more expressive and help you identify and respond to specific issues.
Unhandled Errors: Errors that are not properly caught or handled can lead to unhandled exceptions, which can crash your
We can handle these Errors
Syntax Errors:
Careful Code Review: The best way to handle syntax errors is to prevent them in the first place by reviewing your code thoroughly.
Linter: Use a code linter like ESLint to catch and fix syntax errors automatically.
Runtime Errors:
Debugging: Use Node.js built-in debugging tools like
console.log
or a debugger to inspect variables and trace the issue.Error Handling: Wrap the problematic code in try-catch blocks to catch and handle runtime errors gracefully.
Logging: Log error details to a file or console for debugging purposes.
Logical Errors:
Code Review: Carefully review your code logic, algorithms, and calculations to identify and fix logical errors.
Unit Testing: Write unit tests to validate the correctness of your code and catch logic errors early in development.
Asynchronous Errors:
Promises: Use
.catch()
with promises to handle promise rejections and errors in asynchronous code.Async/Await: Wrap asynchronous code in a try-catch block when using
async/await
.Event Emitters: Add error event listeners to event emitters to catch and handle errors.
System Errors:
Error Handling: Implement robust error handling for system-related errors, like checking for file existence before reading/writing files.
Resource Management: Monitor and manage system resources like memory to avoid running out of resources.
Custom Errors:
- Error Classes: Create custom error classes by extending the
Error
class to represent specific error conditions in your application. This can make error handling more expressive.
- Error Classes: Create custom error classes by extending the
Unhandled Errors:
Global Error Handling: Implement a global error handler using the
process.on('uncaughtException')
event to catch unhandled exceptions and log them.Promise Rejections: Use
process.on('unhandledRejection')
to catch unhandled promise rejections and handle them appropriately.Exit Gracefully: When critical errors occur, consider gracefully shutting down your application to prevent further issues.
Some Practical ways of handling Errors are.
try…catch
blocks
try {
const result = someFunction();
// Use 'result' here
} catch (error) {
// Handle the error
console.error("An error occurred:", error.message);
}
Callbacks
Callbacks are commonly used with the error-first pattern, where the first argument of the callback function is reserved for an error object (if an error occurs), and the subsequent arguments contain the result or data. Here's how you can use callback functions to handle errors:
function readFileAndDoSomething(filePath, callback) {
// Read a file asynchronously
fs.readFile(filePath, 'utf8', (error, data) => {
if (error) {
// Pass the error to the callback
callback(error);
} else {
// Call the callback with the data
callback(null, data);
}
});
}
// Usage
readFileAndDoSomething('example.txt', (error, data) => {
if (error) {
console.error('Error:', error.message);
// Handle the error here
} else {
console.log('File data:', data);
// Continue processing the data here
}
});
I use a Demo .tsxt file for demo purpose
function readFile (filePath,callback){
fs.readFile('data.txt','utf8',(error,data)=>{
if(error){
// passing the error to the callback
callback(error);
}
else{
// call back with data
callback(null,data);
}
})}
readFile('data.txt',(error,data)=>{
if(error){
console.error('Error',error.message)
// handling the error here
}
else{
console.log('File Data',data);
// continue Process
}
})
Promises
Promises have two possible states:
resolved
(when the operation succeeds) andrejected
(when the operation fails).
// Promises
const fs = require('fs');
const fsPromises = require('fs').promises;
fs.promises.readFile ('data.txt').then((result)=>{
console.log("Data " + result)
})
.catch((error)=>{
console.log(error);
})
Promisified Functions:
You can promisify these functions using the
util.promisify
utility in theutil
module (available in Node.js).
// Require this module
const util = require('util');
const readFileAsync = util.promisify(fs.readFile);
readFileAsync('data.txt', 'utf8')
.then((data) => {
console.log('File data:', data);
})
.catch((error) => {
console.error('Error:', error.message);
});
Event Emitter
When an error occurs in your code, you can emit an error event using the
emit
method of your EventEmitter instance.
const EventEmitter = require('events');
const myEmitter = new EventEmitter
// Emit an error event
myEmitter.emit('error', new Error('Something went wrong'));
//Listening for Errors:
myEmitter.on('error', (error) => {
console.error('An error occurred:', error.message);
// Handle the error here
});
const EventEmitter = require('events');
const fileEmitter = new EventEmitter
// create a funciton which read a filePath
function readFileAndEmitevent (filePath){
fs.readFile(filePath,'utf-8',(error,data)=>{
if(error){
// if error occured emit and event
fileEmitter.emit('error',error);
}else{
// emit data event
fileEmitter.emit('data',data);
}
})
}
// attach an error event listner
fileEmitter.on('error',(error)=>{
console.error('An Error couured');
})
// attach an data event listner
fileEmitter.on('data',(data)=>{
console.log('File data:',data);
})
// function call
readFileAndEmitevent('data.txt');
Thank you for reading my content. Be sure to follow and comment on what you want me to write about next
🤓