30. ASYNC | Promise in JavaScript
This article will talk about promise in javascript.
What is a Promise?
It is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
In simple terms:
A Promise is a placeholder for a future result. The result can be a value or an error.
promise.state or promise.result. This will give undefined.Promise States
A Promise has 3 states:
Pending โ Initial state (not completed yet)
Fulfilled (Resolved) โ Successful completion.
Rejected โ Failed operation.
Once a promise is fulfilled or rejected, it is considered settled.
Creating a Promise
const p = new Promise((resolve, reject) => {
let success = true;
if (success) {
// do something
resolve("Task completed.");
} else {
// do something else
reject("Task failed.");
}
});
resolve(value)โ moves promise to fulfilled state.reject(error)โ moves promise to rejected state.
Consuming a Promise
p.then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
}).finally(() => {
console.log("I will always run.");
})
/** Output:
Task completed.
I will always run.
*/
.then()โ runs when a promise is fulfilled..catch()โ runs when a promise is rejected..finally()โ runs always whether a promise is fulfilled or rejected.
Promise Result
Initially undefined, then changes to value if fulfilled (resolve(value)) or error when rejected (reject(error)).
Promise Chaining
Each .then() or .catch() returns a new Promise, enables chaining.
// Creating a promise
const p = new Promise((resolve, reject) => {
let success = true;
if (success) {
resolve("Task completed.");
} else {
reject("Task failed.");
}
});
// Consuming a promise
p.then((data) => {
console.log(data);
return 'shubham';
}).then((data) => {
console.log(data);
return 55;
}).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
}).finally(() => {
console.log("I will always run.");
})
/** Output:
Task completed.
shubham
55
I will always run.
*/
// Creating a promise
const p = new Promise((resolve, reject) => {
let success = false;
if (success) {
resolve("Task completed.");
} else {
reject("Task failed.");
}
});
// Consuming a promise
p.then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
return 'shubham';
}).then((data) => {
console.log(data);
return 55;
}).then((data) => {
console.log(data);
}).finally(() => {
console.log("I will always run.");
})
/** Output:
Task failed.
shubham
55
I will always run.
*/
Attaching multiple handlers
We can attach multiple handlers to one promise. They don't pass the result to each other; instead they process it independently.
let p = new Promise();
p.then(handler1);
p.then(handler2);
p.then(handler3);
How a promise is executed?
Promise is created and pushed onto the Call Stack.
The JavaScript engine removes it from the stack and registers it with the Web API, where it gets either resolved or rejected. Hence, promise runs asynchronously.
If it is resolved, callback function inside
.then()is added to the Microtask queue, while the.catch()handler is ignored. Else, the callback inside.catch()is added to the Microtask Queue, while the.then()handler is ignored.The Event Loop continuously monitors the Call Stack. When the Call Stack becomes empty, it moves callbacks from the Microtask Queue to the Call Stack, one at a time, for execution.
Promise Methods
JavaScript Promise methods are divided into
Instance methods, used for handling the results of a single promise, and
Static methods, used for managing multiple asynchronous operations concurrently.
Instance Methods
These methods are called on an instance of a promise and are primarily used for promise chaining.
.then().catch().finally()
Static Methods (Concurrency)
These methods are called on the Promise class itself and take an iterable (usually an array) of promises as input.
Promise.all(iterable): Fulfills when all input promises fulfill; it "fails fast" and rejects immediately if any single promise in the list rejects.Promise.allSettled(iterable): Waits for all promises to settle (either fulfilled or rejected) and returns an array of objects describing the outcome of each.Promise.any(iterable): Fulfills as soon as any of the input promises fulfill. It only rejects if all promises in the list are rejected.Promise.race(iterable): Settles as soon as the first promise in the iterable settles (either fulfilling or rejecting).
Static Utility Methods
Promise.resolve(value): Returns a promise that is resolved with the given value.
Promise.resolve("shubham")
.then((data) => {
console.log(data)
})
.catch((err) => {
console.log(err)
});
// Output: shubham
Promise.reject(reason): Returns a promise that is rejected with the given reason.
Promise.reject("Rejecting due to some error")
.then((data) => {
console.log(data)
})
.catch((err) => {
console.log(err)
});
// Output: Rejecting due to some error
Why to Use Promises?
Promises are primarily used to manage asynchronous operations in a more structured, readable, and maintainable way than traditional callbacks, effectively solving the problem known as "callback hell".
Key Reasons to Use Promises
Improved Code Readability: Promises allow developers to write asynchronous code in a linear, sequential flow using
.then()and.catch()methods, avoiding deeply nested callback functions (the "pyramid of doom").Better Error Handling: They provide a unified and predictable mechanism for handling errors. A single
.catch()block at the end of a promise chain can handle errors that occur at any step in the sequence, which is much simpler than managing errors at every level of nested callbacks.Sequential Execution (Chaining): Promises can be easily chained together to perform a series of dependent asynchronous operations. Each
.then()method returns a new promise, allowing the next operation to wait for the previous one to complete successfully.Concurrency and Composition: Promises offer static methods like
Promise.all()andPromise.race()to manage multiple asynchronous tasks in parallel, waiting for all or the first one to complete, respectively. This increases efficiency and performance for independent operations.Foundation for
async/await: Promises are the underlying foundation for the modernasync/awaitsyntax. Theasync/awaitkeywords make working with asynchronous operations even more intuitive, allowing code to be written in a style that looks almost exactly like synchronous code while still leveraging the power of promises behind the scenes.Standardization: Many modern web APIs and libraries, such as the Fetch API for network requests, are promise-based by default, making them the standard way to handle asynchronous operations in modern JavaScript development.
Happy reading!

