JavaScript Promises
JavaScript promises are a powerful tool for handling asynchronous operations. A promise represents the eventual completion (or failure) of an asynchronous operation and its resulting value. In this lesson, we'll learn how to create, handle, and chain promises to handle asynchronous workflows effectively.
1. What is a Promise?
A promise is an object representing the eventual completion or failure of an asynchronous operation. It can be in one of three states:
- Pending: The initial state of the promise. The operation is still in progress.
- Fulfilled: The operation completed successfully, and the promise has a resulting value.
- Rejected: The operation failed, and the promise has an error or reason for failure.
2. Creating a Promise
To create a promise, we use the Promise
constructor, which takes an executor function with two parameters: resolve
and reject
.
Basic Syntax of a Promise:
let myPromise = new Promise((resolve, reject) => {
let success = true; // Simulate an asynchronous task
if (success) {
resolve("The task completed successfully!"); // Fulfilled
} else {
reject("The task failed!"); // Rejected
}
});
The resolve
function is called when the task completes successfully, and reject
is called when there is an error or failure.
3. Handling Promise Results
Once a promise is created, we can use the then()
and catch()
methods to handle the resolved value or error, respectively.
Handling a Resolved Promise:
myPromise
.then(result => {
console.log(result); // "The task completed successfully!"
})
.catch(error => {
console.error(error); // Will not run in this example
});
In the example above, the then()
method is called when the promise resolves successfully, and the result is logged to the console.
4. Chaining Promises
Promises can be chained using then()
to perform multiple asynchronous operations sequentially. Each then()
returns a new promise.
Example of Chaining Promises:
let firstTask = new Promise((resolve, reject) => {
setTimeout(() => resolve("First task done!"), 1000);
});
firstTask
.then(result => {
console.log(result); // "First task done!"
return new Promise(resolve => setTimeout(() => resolve("Second task done!"), 1000));
})
.then(result => {
console.log(result); // "Second task done!"
});
In this example, the second promise is created inside the first then()
method and chained to the next then()
.
5. Promise.all
If you need to run multiple asynchronous tasks concurrently and wait for all of them to complete, use Promise.all()
. This method returns a single promise that resolves when all the provided promises are resolved, or rejects when any one of them fails.
Example of Promise.all:
let promise1 = new Promise(resolve => setTimeout(() => resolve("Task 1 completed"), 1000));
let promise2 = new Promise(resolve => setTimeout(() => resolve("Task 2 completed"), 1500));
let promise3 = new Promise(resolve => setTimeout(() => resolve("Task 3 completed"), 500));
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results); // ["Task 1 completed", "Task 2 completed", "Task 3 completed"]
})
.catch(error => {
console.error("An error occurred:", error);
});
In this example, Promise.all()
waits for all three promises to resolve and then executes the then()
block.
6. Promise.race
If you need to perform multiple asynchronous tasks but only care about the first one to resolve (or reject), use Promise.race()
. It returns a promise that resolves or rejects as soon as one of the provided promises resolves or rejects.
Example of Promise.race:
let promise1 = new Promise(resolve => setTimeout(() => resolve("Task 1 completed"), 2000));
let promise2 = new Promise(resolve => setTimeout(() => resolve("Task 2 completed"), 1000));
Promise.race([promise1, promise2])
.then(result => {
console.log(result); // "Task 2 completed"
})
.catch(error => {
console.error(error);
});
In this case, Promise.race()
resolves with the first promise that completes, so the result is "Task 2 completed" because it finished first.
7. Error Handling in Promises
Promises can be rejected, and error handling can be done using catch()
method. It is crucial to handle errors to avoid unhandled rejections, which can lead to bugs and unexpected behavior.
Example of Error Handling:
let myPromise = new Promise((resolve, reject) => {
let success = false;
if (success) {
resolve("Task completed successfully!");
} else {
reject("Something went wrong!");
}
});
myPromise
.then(result => console.log(result))
.catch(error => console.error(error)); // "Something went wrong!"
In this example, if the promise is rejected, the catch()
method will handle the error and log it to the console.
8. Conclusion
JavaScript promises are a powerful way to handle asynchronous operations. By using promises, you can avoid callback hell and handle asynchronous flows in a more structured way. Whether you use simple promises, promise chaining, Promise.all
, or Promise.race
, they allow you to manage asynchronous operations more effectively and with better error handling.