Voiced by Amazon Polly |
Introduction
Handling asynchronous operations is a core part of JavaScript development and promises an elegant way to manage these. Before promises, developers often used callback functions to manage asynchronous tasks, which could lead to confusing and deeply nested structures, commonly called “callback hell.” Promises to streamline this process by providing a more readable and structured approach to handle asynchronous code.
In this guide, we’ll dive into promises, how they work, and how you can use them effectively in your code.
Pioneers in Cloud Consulting & Migration Services
- Reduced infrastructural costs
- Accelerated application deployment
What is a Promise in JavaScript?
A Promise is an object representing an asynchronous operation’s eventual completion (or failure). When an operation begins, the promise is in a “pending” state and will eventually transition to either:
- Fulfilled: The operation was completed successfully.
- Rejected: The operation failed.
Once a promise settles (either fulfilled or rejected), it cannot change its state again.
The Anatomy of a Promise
To understand how promises work, let’s look at the structure of a simple promise:
1 2 3 4 5 6 7 8 9 10 11 |
const myPromise = new Promise((resolve, reject) => { let operationSuccess = true; setTimeout(() => { if (operationSuccess) { resolve("Operation was successful!"); } else { reject("Operation failed."); } }, 2000); }); |
Here’s what’s happening:
- A new Promise object is created, and it takes an executor function with two parameters: resolve and reject.
- If the operation succeeds (simulated by operationSuccess), resolve is called, which transitions the promise to the fulfilled state.
- If the operation fails, reject is invoked, putting the promise into the rejected state.
Handling a Promise's Result
Once a promise is created, it’s important to handle its result using .then() for a successful outcome and .catch() for errors.
1 2 3 4 5 6 7 |
myPromise .then((result) => { console.log(result); // logs: "Operation was successful!" }) .catch((error) => { console.error(error); // In case of rejection, this would log the error message. }); |
- then(): Handles the result of the promise when it resolves successfully. It takes a callback function that gets executed with the resolved value.
- catch(): Handles the error in case the promise is rejected.
These methods allow you to effectively manage successful and failed outcomes of asynchronous tasks.
Promise Chaining
One of the key benefits of promises is that they allow you to chain multiple asynchronous operations together in a clean and readable way. This avoids the deeply nested structures common with callbacks.
For example, if you need to perform a series of asynchronous operations where each step depends on the previous one, you can chain .then() calls:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
const fetchData = new Promise((resolve) => { setTimeout(() => resolve("Data fetched"), 1000); }); fetchData .then((data) => { console.log(data); // "Data fetched" return new Promise((resolve) => setTimeout(() => resolve("Processing data"), 1000)); }) .then((processedData) => { console.log(processedData); // "Processing data" return "Final step completed"; }) .then((finalMessage) => { console.log(finalMessage); // "Final step completed" }) .catch((error) => { console.error("An error occurred:", error); }); |
Each .then() passes its resolved value to the next .then() in the chain, allowing asynchronous tasks to run sequentially.
Promise Methods: Promise.all() and Promise.race()
JavaScript promises also come with utility methods that allow you to manage multiple promises at once:
- Promise.all()
Promise.all() runs multiple promises concurrently and waits for all of them to either resolve or for one to reject. If any promises are rejected, Promise.all() rejects immediately with the first error.
1 2 3 4 5 6 7 8 9 10 11 |
const promise1 = new Promise((resolve) => setTimeout(() => resolve("First promise"), 1000)); const promise2 = new Promise((resolve) => setTimeout(() => resolve("Second promise"), 2000)); const promise3 = new Promise((resolve, reject) => setTimeout(() => reject("Third promise failed"), 3000)); Promise.all([promise1, promise2, promise3]) .then((results) => { console.log(results); // Won't be logged if any promise fails }) .catch((error) => { console.error("Error:", error); // Logs "Third promise failed" }); |
In this case, the failure of any single promise (e.g., promise3) causes the whole operation to fail.
- Promise.race()
Promise.race() also runs multiple promises concurrently but resolves or rejects as soon as one promise settles (whether resolved or rejected).
1 2 3 4 5 6 7 8 9 10 |
const slowPromise = new Promise((resolve) => setTimeout(() => resolve("Slow response"), 3000)); const fastPromise = new Promise((resolve) => setTimeout(() => resolve("Fast response"), 1000)); Promise.race([slowPromise, fastPromise]) .then((result) => { console.log(result); // Logs "Fast response" }) .catch((error) => { console.error("Error:", error); }); |
In this example, Promise.race() resolves as soon as the fastPromise settles, ignoring the slower promise.
Promises and async/await
Although promises provide a cleaner way to handle asynchronous operations, async/await takes it further by making asynchronous code look synchronous. It’s syntactic sugar over promises and is often more intuitive.
Here’s how the earlier example can be rewritten using async/await:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
async function fetchData() { try { const data = await fetchDataPromise(); console.log(data); // "Data fetched" const processedData = await processDataPromise(data); console.log(processedData); // "Processing data" console.log("Final step completed"); } catch (error) { console.error("Error occurred:", error); } } fetchData(); |
The await keyword pauses the execution of the async function until the promise is resolved or rejected, simplifying how you manage asynchronous code.
Conclusion
With methods like .then(), .catch(), and .finally(), promises offer flexibility in controlling the outcomes of operations, while utility functions like Promise.all() and Promise.race() allow for concurrent execution of multiple promises.
Drop a query if you have any questions regarding JavaScript promises and we will get back to you quickly.
Making IT Networks Enterprise-ready – Cloud Management Services
- Accelerated cloud migration
- End-to-end view of the cloud environment
About CloudThat
CloudThat is an award-winning company and the first in India to offer cloud training and consulting services worldwide. As a Microsoft Solutions Partner, AWS Advanced Tier Training Partner, and Google Cloud Platform Partner, CloudThat has empowered over 850,000 professionals through 600+ cloud certifications winning global recognition for its training excellence including 20 MCT Trainers in Microsoft’s Global Top 100 and an impressive 12 awards in the last 8 years. CloudThat specializes in Cloud Migration, Data Platforms, DevOps, IoT, and cutting-edge technologies like Gen AI & AI/ML. It has delivered over 500 consulting projects for 250+ organizations in 30+ countries as it continues to empower professionals and enterprises to thrive in the digital-first world.
FAQs
1. What is the difference between a callback and a promise?
ANS: – A callback is a function passed as an argument to another function, often used for handling asynchronous operations. However, callbacks can lead to nested and hard-to-read code (callback hell). A promise provides a cleaner, more readable approach to handling asynchronous tasks by allowing chaining and better error handling. It can either resolve (successful completion) or reject (failure).
2. Can a promise be resolved or rejected more than once?
ANS: – No, a promise can only be settled (resolved or rejected) once. After that, its state is immutable, meaning it cannot be changed again.

WRITTEN BY Subramanya Datta
Comments