Javascript Promises

Remember when we talked about Destructuring and how it makes our code cleaner? Today, we’re tackling something that used to be the biggest headache for JavaScript developers: Asynchronous operations.
If you've ever tried to fetch data from an API or wait for a timer, you've probably run into "Callback Hell." This is exactly the problem Promises were designed to solve.
Let's break it down in a way that’s easy to digest.
What Problem do Promises Solve?
Before Promises, we used Callbacks. A callback is just a function passed into another function to be run later.
The problem? When you have to do five things in a row (Step 1, then Step 2, then Step 3...), your code starts looking like a sideways pyramid (The "Pyramid of Doom"). It becomes impossible to read and even harder to debug.
The Callback Mess:
getData(function(a) {
getMoreData(a, function(b) {
getEvenMoreData(b, function(c) {
console.log(c); // Good luck finding where the error happened!
});
});
});
Promises fix this by giving us a way to write asynchronous code that looks like it's running line-by-line.
What is a Promise? (A Future Value)
Think of a Promise like ordering a burger at a restaurant:
You order (The Promise is Pending).
The waiter gives you a buzzer. You don't have the burger yet, but you have a promise that it’s coming.
Eventually, the buzzer glows:
Success: You get your burger (The Promise is Fulfilled).
Failure: They ran out of ingredients (The Promise is Rejected).
The 3 States of a Promise
In JavaScript, a promise is always in one of these three states:
Pending: Initial state, neither fulfilled nor rejected.
Fulfilled (Resolved): The operation completed successfully.
Rejected: The operation failed (e.g., network error).
Basic Promise Lifecycle
Here is how you actually create one. It takes a function with two arguments: resolve and reject.
const myPromise = new Promise((resolve, reject) => {
let success = true;
if (success) {
resolve("Operation Successful!");
} else {
reject("Something went wrong...");
}
});
Handling Success and Failure
Once a promise is created, we use .then() to handle the success and .catch() to handle the error.
myPromise.then((data) => {
console.log(data); // "Operation Successful! 🎉" })
.catch((error) => {
console.error(error); // This only runs if reject() was called }
);
Promise Chaining: The Real Power
The coolest thing about promises is that .then() returns a new promise. This allows us to chain them together, keeping our code flat and readable.
The "Clean" Way:
fetchUser(1)
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => console.log(comments))
.catch(err => {
console.log("Caught an error anywhere in the chain:",err);
});
Why use them? (The Benefits)
Readability: No more nested pyramids.
Better Error Handling: One
.catch()at the end can handle errors from any step in the chain.Future Proof: Most modern Web APIs (like
fetch) use Promises by default.
Practice Time!
Remember our laptop example from earlier? Let's say checking the finalPrice takes 2 seconds because it needs to check a database.
Question: How would you write a promise that resolves with the price after 2 seconds?
const checkPrice = new Promise((resolve, reject) => {
setTimeout(() => {
const price = 45000; // Should I use resolve or reject here to return the price?
}, 2000);
});
Hint: Think about the "Burger" analogy. If the price is ready, the buzzer should go off!




