Promises in JavaScript

Modern JavaScript applications perform many tasks that take time.
Examples:
Fetching data from APIs
Reading databases
Uploading files
Authentication systems
Waiting for timers
These operations are asynchronous.
Before promises, developers mainly used callbacks to handle asynchronous tasks.
But callbacks created many problems like:
Nested code
Difficult debugging
Poor readability
To solve these problems, JavaScript introduced Promises.
In this blog, we will learn:
What problem promises solve
Promise states
Promise lifecycle
Handling success and failure
Promise chaining
Internal working of promises
Everything will be explained from basic to advanced in easy language.
What is a Promise?
A Promise is an object that represents:
A future value or future result of an asynchronous operation.
This means:
The value is not available immediately.
It may come later.
Real Life Example
Imagine ordering food online.
When you place the order:
Food is not delivered instantly
Restaurant prepares the food
Delivery arrives later
The food delivery behaves like a promise.
You trust that:
Either food will arrive
Or the order will fail
This is exactly how promises work.
Why Promises Were Introduced
Before promises, callbacks were used.
Example:
loginUser(() => {
getProfile(() => {
getPosts(() => {
getComments(() => {
});
});
});
});
This creates deeply nested code.
This problem is called:
Callback Hell
Problems with callback hell:
Hard to read
Difficult to debug
Difficult maintenance
Pyramid-shaped code
Promises Solve These Problems
Promises improve:
Readability
Error handling
Code structure
Async flow management
Promise Syntax
const promise = new Promise((resolve, reject) => {
});
A promise constructor takes a callback function with two parameters:
resolvereject
Promise States
A promise has three states.
1. Pending
Initial state.
The operation is still running.
2. Fulfilled
The operation completed successfully.
3. Rejected
The operation failed.
Promise Lifecycle Diagram
Promise Created
│
▼
Pending
/ \
/ \
▼ ▼
Fulfilled Rejected
Basic Promise Example
const promise = new Promise((resolve, reject) => {
let success = true;
if(success) {
resolve("Data Loaded");
} else {
reject("Error Loading Data");
}
});
Handling Promise Success
The .then() method handles successful promises.
Example
const promise = new Promise((resolve, reject) => {
resolve("Promise Resolved");
});
promise.then((result) => {
console.log(result);
});
Output
Promise Resolved
Handling Promise Failure
The .catch() method handles rejected promises.
Example
const promise = new Promise((resolve, reject) => {
reject("Something Went Wrong");
});
promise.catch((error) => {
console.log(error);
});
Output
Something Went Wrong
Handling Both Success and Failure
const promise = new Promise((resolve, reject) => {
let success = false;
if(success) {
resolve("Success");
} else {
reject("Failure");
}
});
promise
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
});
Output
Failure
How Promises Work Internally
Step-by-step:
Promise starts in pending state
Async operation runs
If successful → resolve()
If failed → reject()
.then()handles success.catch()handles errors
Promise Execution Flow
Promise Created
│
▼
Async Task Starts
│
▼
Pending State
│
┌─────┴─────┐
▼ ▼
resolve() reject()
▼ ▼
.then() .catch()
Promise Example with setTimeout
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data Received After 2 Seconds");
}, 2000);
});
promise.then((data) => {
console.log(data);
});
Output
Data Received After 2 Seconds
Promises Represent Future Values
When promise is created:
const data = fetch("api-url");
The actual response is not available immediately.
The promise says:
"I will provide the value later."
Promise Chaining
Promises can be chained using multiple .then() methods.
Example
Promise.resolve(5)
.then((num) => {
return num * 2;
})
.then((result) => {
return result + 10;
})
.then((finalResult) => {
console.log(finalResult);
});
Output
20
How Chaining Works
Start Value = 5
│
▼
Multiply by 2
│
▼
10
│
▼
Add 10
│
▼
20
Why Promise Chaining is Useful
Without chaining:
Nested callbacks increase
Code becomes messy
With chaining:
Cleaner flow
Better readability
Easier debugging
Callback vs Promise Comparison
Callback Style
loginUser(() => {
getProfile(() => {
getPosts(() => {
});
});
});
Problems:
Deep nesting
Hard readability
Difficult maintenance
Promise Style
loginUser()
.then(() => getProfile())
.then(() => getPosts())
.catch(err => console.log(err));
Benefits:
Flat structure
Cleaner code
Better error handling
Visual Comparison Diagram
Callbacks:
Task
└── Task
└── Task
└── Task
Promises:
Task
│
▼
.then()
│
▼
.then()
│
▼
.catch()
Promise.catch()
.catch() handles all previous errors in chain.
Example
Promise.resolve("Start")
.then((data) => {
throw new Error("Something Failed");
})
.catch((error) => {
console.log(error.message);
});
Output
Something Failed
Promise.finally()
finally() executes no matter what happens.
Example
Promise.resolve("Done")
.finally(() => {
console.log("Cleanup Completed");
});
Output
Cleanup Completed
Real World API Example
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error);
});
This is how promises are commonly used in APIs.
Promise Methods
| Method | Purpose |
|---|---|
.then() |
Handles success |
.catch() |
Handles errors |
.finally() |
Runs always |
Advanced Promise Methods
JavaScript also provides:
Promise.all()Promise.race()Promise.any()Promise.allSettled()
These help manage multiple promises together.
Promise.all
Example
const p1 = Promise.resolve("A");
const p2 = Promise.resolve("B");
Promise.all([p1, p2])
.then((result) => {
console.log(result);
});
Output
[ 'A', 'B' ]
Benefits of Promises
| Benefit | Explanation |
|---|---|
| Better readability | Cleaner async code |
| Avoid callback hell | Flat structure |
| Better error handling | Centralized catch |
| Chaining support | Organized execution |
| Future value handling | Works with async operations |
Common Mistakes
1. Forgetting Return in Chaining
Wrong:
.then((data) => {
data * 2;
})
Correct:
.then((data) => {
return data * 2;
})
2. Not Handling Errors
Wrong:
fetchData().then(data => console.log(data));
Correct:
fetchData()
.then(data => console.log(data))
.catch(err => console.log(err));
Internal Architecture of Promises
Promise Created
│
▼
Pending State
│
▼
Event Loop Monitors
│
┌─────┴─────┐
▼ ▼
Resolved Rejected
▼ ▼
.then() .catch()
Promises vs Async/Await
Promises are powerful.
But async/await makes promise-based code even cleaner.
Async/await is actually built on top of promises.
Conclusion
Promises are one of the most important concepts in JavaScript.
They solved major problems created by callbacks and made asynchronous programming cleaner and easier.
Promises help developers:
Manage async tasks properly
Handle errors efficiently
Write readable code
Avoid callback hell
Modern JavaScript development heavily depends on promises, and understanding them is essential before learning async/await.
Thank you for reading this blog.
I hope this helped you understand promises in JavaScript from basic to advanced concepts in a simple and practical way.
Your feedback is always appreciated.
If you found this blog useful, feel free to share your thoughts and suggestions for future topics.




