Node JS Error handling in asynchronous code
Error handling in asynchronous code is crucial to ensure that your application can gracefully handle unexpected issues and maintain stability. Asynchronous code can be prone to errors due to various reasons, such as network issues, file system errors, or unexpected responses. Here’s a detailed look at how to handle errors in asynchronous code:
1. Callbacks
Error-First Callbacks:
In Node.js, the conventional approach for error handling with callbacks is to use error-first callbacks, where the first argument of the callback function is reserved for an error object.
Example:
const fs = require('fs');
// Asynchronous file read with error handling
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log('File contents:', data);
});
In this example:
- The
err
parameter is checked to see if it is truthy. If so, an error occurred, and you handle it accordingly. - If
err
is falsy, the operation succeeded, and you process the result.
2. Promises
Handling Errors with .then()
and .catch()
:
With Promises, errors can be handled using the .catch()
method, which is used to handle rejected Promises.
Example:
const fs = require('fs').promises;
fs.readFile('example.txt', 'utf8')
.then(data => {
console.log('File contents:', data);
})
.catch(err => {
console.error('Error reading file:', err);
});
In this example:
.then()
is used to handle successful resolutions of the Promise..catch()
is used to handle any errors that occur during the Promise execution.
Chaining and Error Propagation:
Errors in a Promise chain are propagated to the nearest .catch()
.
Example:
fs.readFile('file1.txt', 'utf8')
.then(data1 => {
return fs.readFile('file2.txt', 'utf8');
})
.then(data2 => {
console.log('File 2 contents:', data2);
})
.catch(err => {
console.error('Error:', err);
});
In this example:
- If an error occurs in any part of the chain, it is caught by the
.catch()
at the end.
3. async
/await
Handling Errors with try
/catch
:
With async
/await
, error handling is done using try
/catch
blocks within asynchronous functions.
Example:
const fs = require('fs').promises;
async function readFile() {
try {
const data = await fs.readFile('example.txt', 'utf8');
console.log('File contents:', data);
} catch (err) {
console.error('Error reading file:', err);
}
}
readFile();
In this example:
try
is used to wrap the code that might throw an error.catch
is used to handle any errors thrown during the execution of thetry
block.
4. Handling Errors with Promise.all()
Handling Multiple Promises:
When using Promise.all()
, if any of the Promises reject, the returned Promise is rejected with the reason of the first rejected Promise.
Example:
const fs = require('fs').promises;
Promise.all([
fs.readFile('file1.txt', 'utf8'),
fs.readFile('file2.txt', 'utf8')
])
.then(([data1, data2]) => {
console.log('File 1 contents:', data1);
console.log('File 2 contents:', data2);
})
.catch(err => {
console.error('Error reading files:', err);
});
In this example:
- If any of the files fail to read, the
catch
block will handle the error.
5. Handling Errors with Promise.race()
Handling the First Resolved or Rejected Promise:
Promise.race()
returns a Promise that resolves or rejects as soon as one of the Promises in the iterable resolves or rejects.
Example:
const fs = require('fs').promises;
Promise.race([
fs.readFile('file1.txt', 'utf8'),
fs.readFile('file2.txt', 'utf8')
])
.then(data => {
console.log('First file contents:', data);
})
.catch(err => {
console.error('Error:', err);
});
In this example:
- The
catch
block will handle any errors if the first Promise to resolve or reject encounters an issue.
6. Common Error Handling Patterns
- Graceful Degradation: Handle errors in a way that allows the application to continue functioning, even if some operations fail.
- User Feedback: Provide meaningful error messages to users or logs for debugging.
- Retry Logic: Implement retry mechanisms for transient errors, such as network issues.
Summary
- Callbacks: Handle errors with error-first callbacks.
- Promises: Use
.catch()
to handle errors in Promise chains. async
/await
: Usetry
/catch
blocks for handling errors in asynchronous functions.Promise.all()
: Handles multiple Promises and catches errors if any of the Promises reject.Promise.race()
: Handles the first resolved or rejected Promise and catches errors accordingly.