Promises and Async/Await in Node.js
Promises
A Promise represents the eventual completion or failure of an asynchronous operation.
Creating Promises
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('Success!');
} else {
reject('Error!');
}
}, 1000);
});
promise
.then(result => console.log(result))
.catch(err => console.error(err));Promise States
- Pending: Initial state
- Fulfilled: Operation completed successfully
- Rejected: Operation failed
Chaining Promises
fetchUser(userId)
.then(user => fetchPosts(user.id))
.then(posts => fetchComments(posts[0].id))
.then(comments => console.log(comments))
.catch(err => console.error(err));Promise Methods
// Promise.all - Wait for all
Promise.all([promise1, promise2, promise3])
.then(results => console.log(results))
.catch(err => console.error(err));
// Promise.race - First to complete
Promise.race([promise1, promise2])
.then(result => console.log(result));
// Promise.allSettled - All results
Promise.allSettled([promise1, promise2])
.then(results => console.log(results));
// Promise.any - First fulfilled
Promise.any([promise1, promise2])
.then(result => console.log(result));Async/Await
Async/await provides a cleaner syntax for working with Promises.
Basic Usage
async function fetchData() {
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (err) {
console.error('Error:', err);
throw err;
}
}Sequential vs Parallel
// Sequential (slower)
async function sequential() {
const user = await fetchUser();
const posts = await fetchPosts();
const comments = await fetchComments();
return { user, posts, comments };
}
// Parallel (faster)
async function parallel() {
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
]);
return { user, posts, comments };
}Error Handling
async function getData() {
try {
const data = await fetchData();
return data;
} catch (err) {
console.error('Error:', err);
throw err;
}
}
// Or with .catch()
async function getData() {
const data = await fetchData().catch(err => {
console.error('Error:', err);
return null;
});
return data;
}Converting Callbacks to Promises
Using util.promisify
const { promisify } = require('util');
const fs = require('fs');
const readFile = promisify(fs.readFile);
async function readData() {
const data = await readFile('file.txt', 'utf8');
console.log(data);
}Manual Conversion
function readFilePromise(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, 'utf8', (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}Real-World Examples
API Calls
async function getUserData(userId) {
try {
const user = await fetch(`/api/users/${userId}`).then(r => r.json());
const posts = await fetch(`/api/posts?userId=${userId}`).then(r => r.json());
return {
...user,
posts
};
} catch (err) {
throw new Error(`Failed to fetch user data: ${err.message}`);
}
}Database Operations
async function createUser(userData) {
try {
const user = await User.create(userData);
await sendWelcomeEmail(user.email);
await logUserCreation(user.id);
return user;
} catch (err) {
console.error('User creation failed:', err);
throw err;
}
}Multiple Operations
async function processOrders() {
try {
const orders = await Order.find({ status: 'pending' });
const results = await Promise.all(
orders.map(async order => {
await processPayment(order);
await updateInventory(order);
await sendConfirmation(order);
return order;
})
);
return results;
} catch (err) {
console.error('Order processing failed:', err);
throw err;
}
}Best Practices
- Always use try-catch with async/await
- Use Promise.all for parallel operations
- Avoid mixing .then() and async/await
- Handle errors at appropriate levels
- Return promises from async functions
Interview Tips
- Explain Promises: Asynchronous operation representation
- Show async/await: Cleaner Promise syntax
- Demonstrate error handling: try-catch blocks
- Discuss Promise methods: all, race, allSettled, any
- Show conversion: Callbacks to Promises
- Mention performance: Sequential vs parallel
Summary
Promises represent asynchronous operations with three states: pending, fulfilled, rejected. Async/await provides cleaner syntax for Promises. Use try-catch for error handling, Promise.all for parallel operations, and util.promisify to convert callbacks to Promises.
Test Your Knowledge
Take a quick quiz to test your understanding of this topic.