Using Promises and Async/Await Effectively in Node.js
Dive into the best use cases for promises and async/await patterns to handle asynchronous operations smoothly.
0 likes
192 views
Rule Content
---
description: Enforce the use of async/await over callbacks and Promises for handling asynchronous operations in Node.js to improve code readability and maintainability.
globs: src/**/*.js
tags: [Node.js, async/await, coding standards]
priority: 2
version: 1.0.0
---
# Using Promises and Async/Await Effectively in Node.js
## Context
- Applicable to all Node.js projects handling asynchronous operations.
- Requires Node.js version 8 or higher.
## Requirements
- **Use async/await for asynchronous operations**: Replace callbacks and Promise chains with async/await syntax to enhance code clarity and reduce nesting.
- **Implement proper error handling**: Utilize try/catch blocks within async functions to manage errors effectively.
- **Avoid mixing callbacks and Promises**: Convert callback-based functions to return Promises, enabling the use of async/await throughout the codebase.
- **Prevent unhandled Promise rejections**: Ensure all Promises are awaited or have catch handlers to handle potential errors.
- **Optimize asynchronous loops**: Use Promise.all() for parallel execution instead of awaiting each operation sequentially within loops.
## Examples
<example>
// Good: Using async/await with proper error handling
async function fetchData() {
try {
const data = await getData();
console.log(data);
} catch (error) {
console.error('Error fetching data:', error);
}
}
</example>
<example type="invalid">
// Bad: Using callbacks leading to callback hell
getData(function(error, data) {
if (error) {
console.error('Error fetching data:', error);
} else {
processData(data, function(error, processedData) {
if (error) {
console.error('Error processing data:', error);
} else {
saveData(processedData, function(error) {
if (error) {
console.error('Error saving data:', error);
} else {
console.log('Data saved successfully');
}
});
}
});
}
});
</example>
<example>
// Good: Converting a callback-based function to return a Promise
const fs = require('fs').promises;
async function readFileAsync(filePath) {
try {
const data = await fs.readFile(filePath, 'utf8');
return data;
} catch (error) {
throw new Error('Error reading file:', error);
}
}
</example>
<example type="invalid">
// Bad: Mixing callbacks and Promises
const fs = require('fs');
function readFileAsync(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
readFileAsync('example.txt', (error, data) => {
if (error) {
console.error('Error reading file:', error);
} else {
console.log(data);
}
});
</example>
<example>
// Good: Using Promise.all() for parallel execution
async function fetchMultipleData(urls) {
try {
const fetchPromises = urls.map(url => fetch(url));
const responses = await Promise.all(fetchPromises);
const data = await Promise.all(responses.map(response => response.json()));
return data;
} catch (error) {
console.error('Error fetching data:', error);
}
}
</example>
<example type="invalid">
// Bad: Awaiting each operation sequentially within a loop
async function fetchMultipleData(urls) {
const data = [];
for (const url of urls) {
try {
const response = await fetch(url);
const jsonData = await response.json();
data.push(jsonData);
} catch (error) {
console.error('Error fetching data from', url, ':', error);
}
}
return data;
}
</example>