JavaScript Promises are a powerful tool for managing asynchronous operations. They simplify the process of handling tasks like API calls, file operations, or timers by providing a cleaner and more readable alternative to callback-based code.
What is a Promise?
A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation. It has three states:
- Pending: The operation has not yet completed.
- Fulfilled: The operation completed successfully, and the promise has a result.
- Rejected: The operation failed, and the promise contains an error.
Creating a Promise
A Promise is created using the Promise
constructor, which takes a function with two arguments: resolve
and reject
.
Example:
const myPromise = new Promise((resolve, reject) => {
let success = true;
if (success) {
resolve("Operation successful!");
} else {
reject("Operation failed!");
}
});
myPromise
.then((result) => console.log(result)) // Output: "Operation successful!"
.catch((error) => console.error(error));
In this example:
resolve
is called when the operation succeeds.reject
is called when the operation fails.
Using Promises
The .then()
Method
The then()
method runs when the promise is resolved (fulfilled).
fetch("https://api.example.com/data")
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error("Error:", error));
The .catch()
Method
The catch()
method handles errors or rejections.
The .finally()
Method
The finally()
method runs after the promise settles, regardless of whether it was fulfilled or rejected.
myPromise
.then((result) => console.log(result))
.catch((error) => console.error(error))
.finally(() => console.log("Promise completed."));
Chaining Promises
Promises can be chained to handle a sequence of asynchronous tasks.
Example:
fetch("https://api.example.com/user")
.then((response) => response.json())
.then((user) => fetch(`https://api.example.com/orders/${user.id}`))
.then((response) => response.json())
.then((orders) => console.log(orders))
.catch((error) => console.error("Error:", error));
Handling Multiple Promises
JavaScript provides methods like Promise.all()
, Promise.allSettled()
, Promise.any()
, and Promise.race()
for working with multiple promises.
Promise.all()
Waits for all promises to fulfill or for one to reject.
const promise1 = Promise.resolve("Promise 1");
const promise2 = Promise.resolve("Promise 2");
Promise.all([promise1, promise2])
.then((results) => console.log(results)) // Output: ["Promise 1", "Promise 2"]
.catch((error) => console.error(error));
Promise.race()
Resolves or rejects as soon as the first promise settles.
Async/Await: A Modern Approach
While Promises improve readability, the async/await
syntax introduced in ES2017 makes working with promises even cleaner.
Example:
async function fetchData() {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Error:", error);
}
}
fetchData();
Common Mistakes with Promises
- Forgetting to Return Promises:
- Always return promises in
.then()
to avoid unexpected results.
- Always return promises in
- Not Handling Errors:
- Use
.catch()
ortry-catch
blocks to handle errors.
- Use
- Nesting Promises:
- Avoid nesting by chaining
.then()
or usingasync/await
.
- Avoid nesting by chaining
Conclusion
Promises are a cornerstone of modern JavaScript, enabling efficient and readable asynchronous programming. Combined with async/await
, they provide a robust way to handle complex workflows. Explore more tutorials and resources at The Coding College.