JavaScript Promises
A Promise in JavaScript is an object that represents the eventual completion (or failure) of an asynchronous operation. Promises help you write cleaner, more readable async code compared to nested callbacks.
What is a Promise?
A Promise is a way to handle something that will finish in the future — like loading data from the internet.
Think of a promise like ordering food at a restaurant. You place your order (start a task), then do other things (like talk with friends) while you wait. When the food is ready, the kitchen gives it to you. A promise works the same way — it "promises" to give you a result later.
A promise has 3 possible states:
- Pending – still waiting for the result.
- Fulfilled – the task was successful, and you got what you wanted.
- Rejected – the task failed, and there was an error.
You use .then() to do something when the promise is successful, and .catch() to handle errors if something goes wrong.
Creating a Promise
In JavaScript, a Promise is a way to handle tasks that take some time to complete, like loading data from a server. You can create your own promise using the new Promise() constructor.
const myPromise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("Task completed!");
} else {
reject("Something went wrong.");
}
});
const myPromise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("Task completed!");
} else {
reject("Something went wrong.");
}
});
The new Promise() constructor takes a function with two parameters: resolve and reject.
- resolve() is called when the task is successful. It tells the promise to mark itself as fulfilled and pass back a result.
- reject() is called when the task fails. It tells the promise to mark itself as rejected and pass back an error message.
In the example above, we check if success is true. If it is, we call resolve("Task completed!"). Otherwise, we call reject("Something went wrong.").
Later, when you use this promise, you can handle the result with .then() and catch errors with .catch().
Using .then() and .catch() with Promises
Once you create a promise, you can handle the result by using .then() for success and .catch() for errors.
myPromise
.then(result => {
console.log("Success:", result);
})
.catch(error => {
console.error("Error:", error);
});
myPromise
.then(result => {
console.log("Success:", result);
})
.catch(error => {
console.error("Error:", error);
});
Here's how it works:
- .then() is called when the promise is resolved (successful). The value passed to resolve() will be available in the then block.
- .catch() is called when the promise is rejected (failed). The error passed to reject() will be available in the catch block.
In the example above, if myPromise is successful, it logs "Success: Task completed!". If something goes wrong, it logs "Error: Something went wrong."
This pattern makes it easy to write clean, readable code when working with asynchronous operations.
Example 1: A Simple Promise with .then() and .catch()
Let's say we want to simulate checking if a user is logged in. We'll use a promise to represent this process.
const isLoggedIn = true;
const checkLogin = new Promise((resolve, reject) => {
if (isLoggedIn) {
resolve("User is logged in.");
} else {
reject("User is not logged in.");
}
});
checkLogin
.then(message => {
console.log("Success:", message);
})
.catch(error => {
console.error("Error:", error);
});
const isLoggedIn = true;
const checkLogin = new Promise((resolve, reject) => {
if (isLoggedIn) {
resolve("User is logged in.");
} else {
reject("User is not logged in.");
}
});
checkLogin
.then(message => {
console.log("Success:", message);
})
.catch(error => {
console.error("Error:", error);
});
Explanation:
- isLoggedIn is set to true, which means the user is logged in.
- We create a promise called checkLogin.
- Inside the promise, we check if isLoggedIn is true.
- If it is, we call resolve() with a success message.
- If it's not, we call reject() with an error message.
- Then, we use .then() to handle the success case and show the message in the console.
- We use .catch() to handle any errors and log them to the console.
Output
Success: User is logged in.
Success: User is logged in.
This is a basic example of how promises work. You use them when something might take time (like checking login, loading data, etc.), and you want to handle both the success and error cases clearly.
Chaining Promises
Sometimes, you need to perform several asynchronous tasks one after another. You can do this by chaining promises using multiple .then() calls.
const firstTask = () => {
return new Promise((resolve, reject) => {
const success = true; // Change this to false to simulate error
if (success) {
resolve("First task done.");
} else {
reject("First task failed.");
}
});
};
const secondTask = (message) => {
return new Promise((resolve, reject) => {
const success = true; // Change this to false to simulate error
if (success) {
resolve(message + " Second task done.");
} else {
reject("Second task failed.");
}
});
};
firstTask()
.then(result => {
console.log(result); // Logs: "First task done."
return secondTask(result);
})
.then(result => {
console.log(result); // Logs: "First task done. Second task done."
})
.catch(error => {
console.error("Error:", error);
});
const firstTask = () => {
return new Promise((resolve, reject) => {
const success = true; // Change this to false to simulate error
if (success) {
resolve("First task done.");
} else {
reject("First task failed.");
}
});
};
const secondTask = (message) => {
return new Promise((resolve, reject) => {
const success = true; // Change this to false to simulate error
if (success) {
resolve(message + " Second task done.");
} else {
reject("Second task failed.");
}
});
};
firstTask()
.then(result => {
console.log(result); // Logs: "First task done."
return secondTask(result);
})
.then(result => {
console.log(result); // Logs: "First task done. Second task done."
})
.catch(error => {
console.error("Error:", error);
});
Here's what happens:
- firstTask() is a function that returns a promise. This promise either resolves successfully with a message or rejects with an error.
- When firstTask() finishes successfully, the first .then() runs. It receives the success message, logs it to the console, and then calls secondTask(), passing that message forward.
- The secondTask() function also returns a promise. When it resolves, the second .then() runs and logs the combined message to the console.
- If either firstTask() or secondTask() encounters an error (by calling reject()), the .catch() at the end catches that error and logs it. This ensures that any failure in the chain is handled gracefully.
Chaining makes it easy to run tasks in order and pass data from one step to the next.
Example 2: Chaining Promises to Simulate Fetching User Data
Imagine you want to get a user's ID first, then use that ID to fetch the user's profile information. Each step returns a promise, and you chain them together.
const getUserId = () => {
return new Promise((resolve, reject) => {
const success = true; // change to false to simulate error
setTimeout(() => {
if (success) {
resolve(101); // User ID
} else {
reject("Failed to get user ID.");
}
}, 1000);
});
};
const getUserProfile = (userId) => {
return new Promise((resolve, reject) => {
const success = true; // change to false to simulate error
setTimeout(() => {
if (success) {
resolve({ id: userId, name: "Tom", age: 25 });
} else {
reject("Failed to get user profile.");
}
}, 1000);
});
};
getUserId()
.then(id => {
console.log("User ID:", id);
return getUserProfile(id);
})
.then(profile => {
console.log("User Profile:", profile);
})
.catch(error => {
console.error("Error:", error);
});
const getUserId = () => {
return new Promise((resolve, reject) => {
const success = true; // change to false to simulate error
setTimeout(() => {
if (success) {
resolve(101); // User ID
} else {
reject("Failed to get user ID.");
}
}, 1000);
});
};
const getUserProfile = (userId) => {
return new Promise((resolve, reject) => {
const success = true; // change to false to simulate error
setTimeout(() => {
if (success) {
resolve({ id: userId, name: "Tom", age: 25 });
} else {
reject("Failed to get user profile.");
}
}, 1000);
});
};
getUserId()
.then(id => {
console.log("User ID:", id);
return getUserProfile(id);
})
.then(profile => {
console.log("User Profile:", profile);
})
.catch(error => {
console.error("Error:", error);
});
How it works:
- getUserId() simulates fetching a user ID with a 1-second delay.
- If successful, it resolves with the user ID (e.g., 101); otherwise, it rejects with an error.
- The first .then() logs the user ID and calls getUserProfile() with that ID.
- getUserProfile() simulates fetching user details with another 1-second delay.
- The second .then() receives and logs the user profile object.
- If either step fails, the .catch() block logs the error message.
Output
User ID: 101
User Profile: { id: 101, name: "Tom", age: 25 }
User ID: 101
User Profile: { id: 101, name: "Tom", age: 25 }
This example shows how chaining promises helps you perform dependent asynchronous tasks in order.
Frequently Asked Questions
What is a JavaScript Promise?
What is a JavaScript Promise?
A JavaScript Promise is an object that represents a task that will finish in the future. It helps handle async actions like loading data or waiting for something to finish.
What are the states of a Promise?
What are the states of a Promise?
A Promise can be in one of three states: pending (still running), fulfilled (completed successfully), or rejected (failed with an error).
How do I use .then() and .catch() with Promises?
How do I use .then() and .catch() with Promises?
Use .then() to run code when a Promise is successful, and .catch() to handle any errors if it fails.
Can Promises replace callbacks?
Can Promises replace callbacks?
Yes, Promises are often used instead of callbacks because they make async code easier to read and manage, especially when doing multiple steps in a row.
What is Promise chaining?
What is Promise chaining?
Promise chaining means using .then() multiple times in a row to run tasks one after another in order. Each .then() runs after the previous one finishes.
What's Next?
Next, learn about async/await, a cleaner way to write promise-based code using modern JavaScript syntax.