JavaScript Promises Explained for Beginners

You write async code like this…
getUser(function(user) {
getPosts(user.id, function(posts) {
getComments(posts[0].id, function(comments) {
console.log(comments);
});
});
});
Looks confusing right? This is called callback hell
Now look at this:
getUser()
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => console.log(comments))
.catch(err => console.log(err));
Clean, Readable, Easy to debug
That’s why Promises exist
This is the Problem Promises Solve
Before Promises:
Deep nested code (callback hell)
Hard to read and maintain
Error handling is messy
Debugging is painful
Example Problem
Imagine:
Get user
Then get posts
Then get comments
With callbacks → nested pyramid
With promises → straight flow
What is a Promise?
A Promise is:
A value that will be available in the future
Imagine:
Ordering food online:
Order placed → Pending, Delivered → Fulfilled, Cancelled → Rejected
That’s exactly how Promises behave
Important
Promise is: Not immediate, Asynchronous, Future-based value
Promise States - IMP
1. Pending
Initial state, Work is still happening
Example: API request is in progress
2. Fulfilled
Operation successful, Result is returned
Example: Data received from server
3. Rejected
Operation failed
Error is returned
Example: Network error
Visual Flow
Pending → Fulfilled
→ Rejected
Creating a Promise:
Basic Syntax
const promise = new Promise((resolve, reject) => {
let success = true;
if (success) {
resolve("Data received");
} else {
reject("Error occurred");
}
});
Explanation:
new Promise()→ create promiseresolve()→ successreject()→ failure
Think Like This
resolve → success path
reject → error path
Handling Success and Failure
Using .then() and .catch()
promise
.then(result => {
console.log(result);
})
.catch(error => {
console.log(error);
});
Explanation
.then()→ runs when success happens.catch()→ runs when error happens
Mental Model
Promise = “If this works → do this, else → do that”
Promise Lifecycle (Full Flow)
Create → Pending → (resolve/reject) → then/catch
Flow Example
fetchData()
.then(data => processData(data))
.then(result => console.log(result))
.catch(err => console.log(err));
What happens here?
First async task runs
When done →
.then()runsNext step runs
If error →
.catch()handles
Promise Chaining (VERY IMPORTANT)
Without chaining (bad)
Nested again:
getUser(function(user) {
getPosts(user.id, function(posts) {
console.log(posts);
});
});
With chaining (good)
getUser()
.then(user => getPosts(user.id))
.then(posts => console.log(posts))
.catch(err => console.log(err));
Why chaining is powerful?
No nesting
Easy to read
Step-by-step logic
Better error handling
Rule
Always return inside .then()
.then(user => {
return getPosts(user.id);
})
Callback vs Promise
Callback Style
login(user, function(data) {
getProfile(data, function(profile) {
console.log(profile);
});
});
Promise Style
login(user)
.then(data => getProfile(data))
.then(profile => console.log(profile))
.catch(err => console.log(err));
Difference
| Callback | Promise |
|---|---|
| Nested | Flat |
| Hard to read | Easy |
| Multiple error handling | Single catch |
Mental Model (Best Way to Remember)
Promise = Pipeline
Step 1 → Step 2 → Step 3 → Result
Each .then() = next step
Final Thought
Promises don’t make async code faster.
They make it understandable




