Introduction to Promises
JavaScript Promises are a powerful way to handle asynchronous operations like network requests, timers, or file access. A Promise represents a value that may be available now, later, or never.
Creating a Promise
A Promise is created using the Promise
constructor, which takes a function with two arguments: resolve
and reject
.
const fetchData = () => {
return new Promise((resolve, reject) => {
let success = true;
setTimeout(() => {
if (success) {
resolve("Data fetched successfully!");
} else {
reject("Failed to fetch data.");
}
}, 1000);
};
fetchData()
.then((data) => console.log(data))
.catch((error) => console.error(error));
Output:
Data fetched successfully!
Understanding the Output
In the above example, we used setTimeout
to simulate an asynchronous operation. If success
is true, the promise is resolved. Otherwise, it's rejected. The .then()
method handles the success case, and .catch()
handles errors.
Why use Promises?
Without Promises, asynchronous code becomes hard to manage, especially when nested. Promises help avoid callback hell by providing a cleaner and more readable way to handle async operations.
Chaining Promises
One of the main strengths of Promises is chaining. You can return values from a .then()
block and pass it to the next .then()
.
const stepOne = () => {
return new Promise((resolve) => {
setTimeout(() => resolve("Step 1 complete"), 1000);
});
};
const stepTwo = (message) => {
console.log(message);
return new Promise((resolve) => {
setTimeout(() => resolve("Step 2 complete"), 1000);
});
};
const stepThree = (message) => {
console.log(message);
return new Promise((resolve) => {
setTimeout(() => resolve("Step 3 complete"), 1000);
});
};
stepOne()
.then(stepTwo)
.then(stepThree)
.then((finalMessage) => console.log(finalMessage));
Output:
Step 1 complete Step 2 complete Step 3 complete
Explanation
Each function returns a Promise. As each Promise resolves, the next one in the chain is called. This pattern is great for running asynchronous tasks in sequence.
What happens if something fails in the chain?
If a Promise in the chain is rejected, the control jumps to the nearest .catch()
block.
const faultyStep = () => {
return new Promise((resolve, reject) => {
setTimeout(() => reject("Something went wrong!"), 1000);
});
};
stepOne()
.then(faultyStep)
.then(stepThree)
.catch((error) => console.error("Error caught:", error));
Output:
Step 1 complete Error caught: Something went wrong!
Questions to Develop Intuition
Q1: What happens if you forget to return a Promise in a .then()
?
A: The next .then()
in the chain receives undefined
. This can lead to bugs if you're expecting a value or further async operation.
Q2: Can you chain a .catch()
after a .then()
?
A: Yes. If any previous Promise is rejected, the .catch()
block handles the error.
Q3: Is it mandatory to use catch()
at the end of a chain?
A: It's not mandatory, but it's good practice to avoid unhandled Promise rejections.
Summary
- A Promise is an object that represents a future value.
.then()
is used to handle resolved values,.catch()
for errors.- Chaining allows running async tasks sequentially in a readable way.
- Always return Promises when chaining to avoid unexpected behavior.