Understanding Promises in Javascript


Before understanding the promise, we first need to understand some terminology related to it.
Callback: A callback is a function that is provided as an argument to another function. It is executed after the main function has finished its task.
Asynchronous: An asynchronous action is a task that runs in the background and completes later, allowing the main program to keep running without waiting for it.
Settle the promise: A Promise is considered settled when it is either resolved or rejected. In other words, to finish its pending state.
Executor: The executor is the function inside a
Promise
that starts the asynchronous operation and calls the callbackresolve
orreject
after the asynchronous function has completed its execution to settle the promise.Consumer: In JavaScript (especially with Promises), a consumer is the code that uses or handles the result of an asynchronous operation, typically using
.then()
,.catch()
, orawait
.Clean-up in promises: Clean up in promises means doing a final task after the promise is finished, whether it was successful or failed.
If you're in a JavaScript interview and the interviewer asks, "What is a Promise?", and you want to respond with a clean, to-the-point answer, here's a typical one-liner that does the job
A Promise is a JavaScript object that represents the result of an asynchronous operation, allowing you to handle success or failure in a structured and non-blocking way.
But wait, Promises are more than that. Let's take a deep dive into Promises with Developer Atul.
Let's first understand what "JavaScript object that represents the result of an asynchronous task" means.
const IPromise = new Promise((resolve, reject)=>{
})
console.log(IPromise);
This is the output of the above code directly from my browser console.
This is what that special JavaScript object looks like.
Okay, but you may ask:
What does this Eventual Completion mean?
What we need to do after Completion?
What's so special about it? It looks like a normal JavaScript object.
Let's understand these questions.
In the output above, you can see [[PromiseState]]: "pending"
. This is one of the three states a promise can be in. The other two states are rejected
and resolved
. Let’s see the details.
Pending: When we create a promise, we pass an executor function to the constructor. Behind the scenes, the constructor immediately calls the executor function, with resolve and reject callbacks and sets the state to pending.
Fulfilled: When the promise is successfully resolved, it will call the
resolve
callback and change the state to fulfilled .Rejected: When there is any error occur in the executer function then
reject
callback will be called and change the state to rejected.
Below code help you understand this more clearly.
// Fake custom Promise to simulate how it works
function MyPromise(executor) {
// internal state
let state = "pending";
let value = undefined;
// internal resolve function
function resolve(result) {
if (state === "pending") {
state = "fulfilled";
value = result;
console.log("Promise resolved with:", value);
}
}
// internal reject function
function reject(error) {
if (state === "pending") {
state = "rejected";
value = error;
console.log("Promise rejected with:", value);
}
}
// ✅ Constructor calls executor immediately
try {
executor(resolve, reject); // This line is the key!
} catch (err) {
reject(err); // If executor throws, promise is rejected
}
}
// Usage
const p = new MyPromise((resolve, reject) => {
console.log("Executor function is running");
resolve("Data loaded");
});
The above explanation gives a clear picture of what 'eventual completion' means. it will wait for the result to come in an asynchronous function.
To answer rest two question we need to understand other methods of our special object. we have seen how constructor works and state of promise. now let see what are other methods.
Then: The
.then()
method is called when a Promise is successfully fulfilled, and it is part of the consumer interface of a Promise, used to define what should happen next with the resolved value.Catch: The
catch()
method is part of the consumer interface of a Promise. It is used to handle errors or rejections that occur during the asynchronous operation or anywhere in the promise chain.The
catch()
method is called when the Promise is rejected using thereject()
function inside the executor. It is also triggered if an error is thrown inside any of thethen()
blocks in the promise chain.Finally: The
finally()
method is part of the consumer interface of a Promise and is used to perform clean-up work after the Promise is settled, regardless of whether it was fulfilled or rejected.
One of the most powerful features of the Promise consumer interface is that you can chain .then()
, .catch()
, and .finally()
methods one after another.
// A function that returns a Promise
function doAsyncTask() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve("Task completed");
} else {
reject("Task failed");
}
}, 1000);
});
}
// Consuming the Promise using chaining
doAsyncTask()
.then(result => {
console.log("Step 1:", result); // "Task completed"
return result + " → Step 2 done";
})
.then(updatedResult => {
console.log("Step 2:", updatedResult);
return updatedResult + " → Step 3 done";
})
.then(finalResult => {
console.log("Final result:", finalResult);
})
.catch(error => {
console.error("Caught error:", error);
})
.finally(() => {
console.log("Clean-up: This runs no matter what");
});
The above code gives the following output. First, let's see the output, and then we’ll discuss it
In the output we can see the code is executed successfully and in sequential manner. We can also see the state as 'fulfilled' — this means it’s a live object
A live object means the output (like a list or collection) is linked to the original data and reflects real-time changes, without needing to re-query or refresh it.
In the above output we can also see we can chain .then()
multiple times and .catch()
to catch any error in any stage and also run clean-up using .finally
It will run no matter the result of the Promise..
Now we reached to the point where we can confidently say that Promise is indeed a special object.
It tracks asynchronous state
A regular object stores static data.
A Promise tracks a dynamic state:
pending
→fulfilled
orrejected
.
It has built-in methods
It comes with powerful methods like:
.then()
– handles success.catch()
– handles errors.finally()
– handles clean-up
These methods let you "chain" actions in a smooth and readable way.
It automatically executes
- When you create a Promise with
new Promise()
, the executor function runs immediately — this isn't the case with regular objects.
- When you create a Promise with
It settles only once
- Once a Promise is either resolved or rejected, its state cannot change again — this one-time state change is enforced internally by JavaScript.
It enables better async handling
- It’s deeply integrated with
async/await
, modern syntax for writing asynchronous code in a synchronous style.
- It’s deeply integrated with
We’ll take a deep dive into async/await in the next article. Till then...
Happy Coding
Subscribe to my newsletter
Read articles from Atul Chourasiya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
