What are JavaScript Promises?
Promises are essential building blocks for handling asynchronous operations in JavaScript. In JavaScript, a Promise is a special object that represents the eventual completion (or failure) of an asynchronous operation. It is used to handle operations that take an unknown amount of time to complete, such as network requests or database queries.
The benefits of using Promises in JavaScript are as follows:
Improves Code Readability: Promises provide a more readable and organized way to write asynchronous code compared to traditional callback-based approaches. Promises allow you to chain multiple asynchronous operations together, making the code easier to understand and maintain.
Better Handling of Asynchronous Operations: Promises provide a structured way to handle the results of asynchronous operations. With Promises, you can specify what should happen when an operation completes successfully (resolved) or when it fails (rejected), making it easier to handle different outcomes.
Better Flow of Control Definition in Asynchronous Logic: Promises help define the flow of control in asynchronous logic more explicitly. By chaining promises together, you can specify the order in which asynchronous operations should be executed, making the code more predictable and manageable.
Better Error Handling: Promises have built-in error handling capabilities. If an error occurs during the execution of a promise, it can be caught and handled using the catch method. This allows for centralized error handling and reduces the risk of unhandled errors in your code.
However, there are also some drawbacks associated with Promises in JavaScript:
Promises have an API that encourages casually dangerous code: Promises provide a powerful API, but it also allows for potentially dangerous practices if not used carefully. For example, Promises can lead to "promise hell" if not properly structured or if too many nested promises are used.
Promises lack a convenient API to safely work with data: Working with data within Promises can sometimes be cumbersome, as Promises themselves do not provide a convenient API for manipulating data. Additional utility libraries or language features like async/await are often used to work with Promises more conveniently.
Promises co-mingle rejected promises and unintended runtime exceptions: When working with Promises, there is a possibility of mixing rejected promises and unintended runtime exceptions. This can make error handling more challenging, as both types of errors need to be handled differently.
Syntax:
The syntax for creating a Promise in JavaScript is as follows:
let promise = new Promise(function(resolve, reject) {
// Make an asynchronous call and either resolve or reject
});
A Promise can be in one of the following states:
Pending: This is the initial state of a Promise when it is neither fulfilled nor rejected. It represents an ongoing asynchronous operation.
Fulfilled/Resolved: This state indicates that the operation was completed successfully, and the Promise has a resulting value. The resolve function is called to transition the Promise to this state.
Rejected: This state indicates that the operation failed, and the Promise has a reason for the failure. The reject function is called to transition the Promise to this state.
Settled: A Promise is said to be settled when it has either been fulfilled or rejected. Once settled, the Promise will remain in that state and cannot transition to any other state.
Working with Promises
Promises are a powerful tool in JavaScript that greatly enhance the handling of asynchronous operations, leading to improved code readability and providing better control flow and error handling capabilities.
A promise represents the eventual completion (or failure) of an asynchronous operation. It is in one of three possible states: pending, fulfilled, or rejected. A pending promise is one that is still undergoing an asynchronous operation and has not yet been fulfilled or rejected.
Once a promise is fulfilled, it means the asynchronous operation has successfully completed, and it carries a resulting value. On the other hand, when a promise is rejected, it means the operation has encountered an error or failure, and it holds a reason or an error message.
Promises allow you to attach handlers to be executed when the promise is fulfilled or rejected. These handlers are registered using the then method of the promise. If a handler is attached to a promise that has already been fulfilled or rejected, the handler will be immediately called, ensuring that there is no race condition between the completion of an asynchronous operation and the execution of its associated handlers.
A promise is considered to be settled when it has transitioned from the pending state to either the fulfilled state or the rejected state. Once a promise is settled, it means that the asynchronous operation it represents has been completed, and its associated handlers have been called or will be called.
A settled promise can be in one of two states:
Fulfilled/Resolved: This state indicates that the asynchronous operation was completed successfully, and the promise carries a resulting value. The handlers registered using the then method can access and process this value.
Rejected: This state indicates that the asynchronous operation encountered an error or failure. The promise holds a reason or an error message explaining the cause of the rejection. The handlers registered using the catch or then method with a rejection handler can handle and respond to this error.
Methods of Promises
1.Promise.all()
Waits for all the promises to be resolved or any to be rejected. If the returned promise resolves, it is resolved with an array of all the values from the resolved promises, in the same order as defined.
const promise1 = Promise.resolve(3);
const promise2 = 42; Promise.all([promise1,promise2]).then(value=>console.log(value)); //Output:[3,42]
If it rejects, it is rejected with the reason from the first promise as seen in the example below.
const promise1 = Promise.reject("promise.all() error");
const promise2 = 42; const promise3 = Promise.reject(); Promise.all([promise1,promise2,promise3]).then(value=>console.log(value));
//Output: promise.all() error
2.Promise.all settled()
The Promise.allSettled() the method returns a promise that resolves after all of the given promises have either been fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
setTimeout(reject, 100, "foo") );
const promises = [promise1, promise2]; Promise.allSettled(promises).then((results) => results.forEach((result) =>
console.log(result.status)) );
//Output: fulfilled
// rejected
3.Promise.any()
Promise.any() takes an iterable of Promise objects. It returns a single promise that resolves as soon as any of the promises in the iterable fulfills, with the value of the fulfilled promise. If no promises in the iterable fulfill (if all of the given promises are rejected), then the returned promise is rejected.
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));
const promises = [promise1, promise2, promise3]; Promise.any(promises).then((value) =>
console.log(value));
// Output: "quick"
4.Promise.race()
The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.
const promise1 = new Promise((resolve, reject) =>
{
setTimeout(resolve, 100, 'one');
});
const promise2 = new Promise((resolve, reject) =>
{
setTimeout(resolve, 500, 'two');
});
Promise.race([promise1, promise2]).then((value) =>
{
console.log(value);
// Both resolve, but promise1 is faster });
// expected output: "one"
5.Promise.reject()
The Promise.reject() method returns a Promise object that is rejected with a given reason.
function resolved(result)
{
console.log('Resolved');
}
function rejected(result)
{
console.error(result);
}
Promise.reject(new Error('fail')).then(resolved, rejected);
// expected output: Error: fail
6.Promise.resolve()
Returns a new Promise object that is resolved with the given value. If the value has a then method, the returned promise will “follow” that then, adopting its eventual state; otherwise, the returned promise will be fulfilled with the value.
Promise.resolve('Success').then(function(value)
{
console.log(value); // "Success"
}, function(value) {
});
Conclusion
Promises allow you to work with asynchronous operations in a more structured and organized manner. By utilizing Promises, you can chain multiple asynchronous operations together, specifying the order in which they should be executed. This leads to code that is easier to understand, maintain, and reason about.
Comments