Promises fix callback hell. A Promise is an object that represents the eventual completion (or failure) of an async operation. It can be in one of three states: pending, fulfilled, or rejected.
Creating a Promise
const fetchData = new Promise((resolve, reject) => {
setTimeout(() => {
const data = { id: 1, name: "Alice" };
resolve(data); // Success
// reject(new Error("Failed")); // Failure
}, 1000);
});
Consuming Promises
fetchData
.then(data => {
console.log("Data:", data);
return data.name;
})
.then(name => {
console.log("Name:", name);
})
.catch(error => {
console.error("Error:", error);
})
.finally(() => {
console.log("Done!");
});
Try it Yourself →
Converting Callbacks to Promises
const fs = require("fs").promises;
// Promise-based file reading
async function readFile() {
try {
const data = await fs.readFile("data.txt", "utf-8");
console.log(data);
} catch (err) {
console.error(err);
}
}
// Promisify callback-based functions
const { promisify } = require("util");
const readFile = promisify(fs.readFile);
Promise.all and Promise.race
// Run multiple promises in parallel
const results = await Promise.all([
fetch("/api/users"),
fetch("/api/posts"),
fetch("/api/comments")
]);
// Use first to complete
const fastest = await Promise.race([
fetch("/api/server1"),
fetch("/api/server2"),
]);
// Wait for all, handle individual results
const settled = await Promise.allSettled([
fetch("/api/option1"),
fetch("/api/option2"),
]);
Best Practice: Always return Promises from .then() handlers so you can chain them. And always handle errors with .catch() — unhandled Promise rejections crash your Node.js process.