For working professionals
For fresh graduates
More
I believe that JavaScript's asynchronous nature, while powerful, often leads to convoluted code littered with nested callbacks. This compromises readability, error handling, and maintainability. I know this from prior experience handling unorganized chunks of asynchronous code.
Callbacks within callbacks formed a labyrinth in my projects. According to me, error handling transformed into a game of chance, and readability was extremely low.
JavaScript promises offer a solution for all of these. They provide a structured approach to managing the results of asynchronous operations, promoting cleaner code and more reliable asynchronous workflows. Promises encapsulate success and failure states, allowing for consistent error handling and the ability to chain asynchronous actions in a more intuitive manner.
If you're looking to streamline your JavaScript code and gain a stronger grasp of asynchronous patterns, understanding promises is crucial. Let us learn more with this JavaScript promise tutorial.
In JavaScript, promises are objects designed to represent the eventual outcome of an asynchronous operation. This eventual outcome can be either a successful result or an error.
Promises have three primary states:
Promises provide a significant improvement over traditional callback-based approaches for handling asynchronous code in JavaScript.
Here's why:
Using JavaScript promises is extremely simple. To newly create a JavaScript promise, you utilize the new Promise() constructor. This constructor function accepts a single argument, an executor function that takes two callback parameters, customarily named resolve and reject.
The executor function is where you place the code responsible for the asynchronous operation. The resolve function is called when the operation completes successfully. The reject function is called if the operation fails.
Successful JavaScript promise example:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Success!"), 1000); // Simulate a 1-second delay
});
Failed JavaScript promise example:
const myPromise = new Promise((resolve, reject) => {
const error = new Error("Something went wrong");
reject(error);
});
The JavaScript promise method has many practical applications. Let us check out some common uses of the JavaScript promise function.
Example:
function fetchData(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(new Error('Network Error'));
xhr.send();
});
}
Example:
function delay(time) {
return new Promise((resolve, reject) => {
setTimeout(resolve, time);
});
}
Example:
function getUserInput(promptMessage) {
return new Promise((resolve, reject) => {
const inputValue = prompt(promptMessage);
if (inputValue) {
resolve(inputValue);
} else {
reject(new Error('User did not provide input'));
}
});
}
Let us check out some practical Javascript promise object examples and projects that you can try out yourself.
index.html code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise Image Loader</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="image-container"></div>
<script src="script.js"></script>
</body>
</html>
styles.css code:
#image-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 300px;
border: 1px solid #ccc;
}
#image-container .error-message {
color: red;
}
script.js code:
function loadImage(imageUrl) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve(image);
image.onerror = (error) => reject(error);
image.src = imageUrl;
});
}
loadImage('https://example.com/myimage.jpg')
.then((image) => {
document.body.appendChild(image);
})
.catch((error) => {
console.error("Error loading image:", error);
});
index.html code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Promise Delay</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="output"></div>
<script src="script.js"></script>
</body>
</html>
styles.css code:
#output {
min-height: 80px;
border: 1px solid #ddd;
padding: 10px;
}
script.js code:
function delay(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
const outputDiv = document.getElementById('output');
delay(1000)
.then(() => {
outputDiv.textContent = "After 1 second";
return delay(2000);
})
.then(() => {
outputDiv.textContent = "After 3 seconds total";
});
Let's refactor an AJAX request using callbacks to a cleaner promise-based approach with .then().
Callback approach:
function fetchDataWithCallback(url, callback) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
callback(null, xhr.responseText);
};
xhr.onerror = function() {
callback(new Error('Network Error'), null);
};
xhr.send();
}
fetchDataWithCallback('https://api.upGrad.com/data', (error, data) => {
if (error) {
console.error(error);
} else {
console.log(data);
}
});
Promise-based approach:
function fetchDataWithPromise(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
resolve(xhr.responseText);
};
xhr.onerror = function() {
reject(new Error('Network Error'));
};
xhr.send();
});
}
fetchDataWithPromise('https://api.upGrad.com/data')
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error);
});
This promise-based approach offers improved readability, easier error handling, and the ability to chain further asynchronous operations using .then().
The .then() method is used to chain a callback function that executes when a promise is successfully fulfilled (resolved). .then() takes a callback function as an argument, which receives the resolved value from the promise. Also, .then() returns a new promise, allowing you to chain multiple .then() calls for sequential asynchronous operations.
Example:
myPromise.then((result) => {
console.log(result); // Will log "Success!" after 1 second
}).catch((error) => {
// This will not be called since myPromise was successful
});
The .catch() method is used to chain a callback function that executes when a promise is rejected. .catch() takes a callback function as an argument, which receives the error object from the rejected promise. This method is crucial for error handling and prevents errors from going unnoticed in promise chains.
Example:
myPromise.then((result) => {
// ... do something with result
}).catch((error) => {
console.error("An error occurred:", error);
});
We can chain multiple .then() calls to handle a sequence of successful asynchronous operations. I would also like to mention that any error in a .then() chain will be caught by the next .catch() or by a global error handler if no .catch() is present.
When you return a promise from within a .then() callback, the next .then() in the chain will wait for that returned promise to resolve before executing. This is the foundation of composing asynchronous sequences.
Example:
function getUserData() {
return new Promise((resolve) => {
setTimeout(() => resolve({ userId: 1 }), 1000); // Simulate fetching user data
});
}
function fetchUserPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => resolve(['Post 1', 'Post 2']), 500); // Simulate fetching posts
});
}
function displayPosts(posts) {
console.log(posts);
}
// Chaining asynchronous operations:
getUserData()
.then((userData) => fetchUserPosts(userData.userId))
.then(displayPosts)
.catch((error) => console.error("An error occurred:", error));
In the above example, getUserData simulates an asynchronous operation to fetch user data. It returns a promise. The first .then() receives the resolved userData. We return a new promise, fetchUserPosts, using the userData.userId.
The second .then() waits for fetchUserPosts to resolve. It then passes the resulting posts to displayPosts. Finally, the .catch() handles errors anywhere in the chain. When we are composing promise chains, we should always handle potential errors with either .catch() or by returning rejected promises from within your .then() callbacks.
Promise.all() executes a set of promises concurrently and returns a single promise that resolves when all input promises have resolved.
Promise.all() takes an iterable of promises (array, etc.) as input and returns a new promise that resolves to an array containing the resolved values of all input promises (in the same order). This method also rejects if any of the input promises are rejected, with the rejection reason being the error from the first rejected promise.
Syntax:
Promise.all(iterableOfPromises)
Example:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise 1 resolved"), 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise 2 resolved"), 2000);
});
Promise.all([promise1, promise2])
.then((results) => {
console.log(results); // ["Promise 1 resolved", "Promise 2 resolved"]
})
.catch((error) => {
console.error(error);
});
Promise.race() executes a set of promises concurrently and returns a single promise that resolves or rejects as soon as any of the input promises resolves or rejects.
Promise.race() takes an iterable of promises as input and returns a new promise that resolves or rejects based on the first settled input promise (whichever resolves or rejects first) This method resolves with the resolved value of the first resolved promise and rejects with the rejection reason of the first rejected promise.
Syntax: Promise.race(iterableOfPromises)
Example:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise 1 resolved"), 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Promise 2 rejected")), 500);
});
Promise.race([promise1, promise2])
.then((result) => {
console.log(result); // Will not be called because promise2 rejects first
})
.catch((error) => {
console.error(error); // Logs "Error: Promise 2 rejected"
});
Async/Await functions always return a promise, and the await keyword can only be used inside functions declared with async. Async/Await is primarily a way to make asynchronous code look and behave more like synchronous code, even though it's still asynchronous under the hood.
There are two key components:
1. async function: You declare a function as async to enable the use of the await keyword within it.
Syntax example:
let result = await somePromiseReturningFunction();
2. await keyword: The await keyword can be placed in front of a promise-returning function. It pauses the execution of the async function until the promise is resolved. The await expression returns the resolved value of the promise.
Syntax example:
let result = await somePromiseReturningFunction();
If you wish to master JS and become a full stack developer, you can check out upGrad’s full stack development courses.
I hope that I got the fundamentals of JavaScript promises explained to you properly in this tutorial. If you wish to learn more about JS, you can check out the other JS tutorials, or, you can simply join upGrad’s software development courses to start mastering all the essential tools.
1. What is a JavaScript promise?
A JavaScript promise is an object representing the eventual completion (success or failure) of an asynchronous operation.
2. What are the 3 promises in JavaScript?
- Pending: The initial state, before completion or failure.
- Fulfilled: The operation completed successfully, and a result is available.
- Rejected: The operation failed, and an error reason is available.
3. What is an example of a promise?
const myPromise = new Promise((resolve, reject) => {
// Simulate fetching request with a timeout
setTimeout(() => {
resolve("Fetched data successfully!");
}, 2000);
});
4. Why promise is better than callback?
Promises improve asynchronous code readability, provide built-in error handling mechanisms, and simplify chaining of asynchronous operations, avoiding the "callback hell" problem.
5. Why use JavaScript promises?
Use JavaScript promises to manage asynchronous operations in a structured way, enhance error management, and make your code more maintainable.
6. What is async and Promise in JavaScript?
7. What is callback in JS?
A callback in JS is a function passed as an argument to another function, to be executed later after an event or task completes.
8. Is async better than Promise?
Async/await is syntactic sugar built upon promises, making asynchronous code look more synchronous; it's not about "better" but rather, about cleaner style in many scenarios.
Mukesh Kumar
Working with upGrad as a Senior Engineering Manager with more than 10+ years of experience in Software Development and Product Management and Pro…Read More
Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)
Indian Nationals
1800 210 2020
Foreign Nationals
+918045604032
1.The above statistics depend on various factors and individual results may vary. Past performance is no guarantee of future results.
2.The student assumes full responsibility for all expenses associated with visas, travel, & related costs. upGrad does not provide any a.