What is a Promise?
JavaScript is a single-threaded programming language, which means only one thing can happen at a time. Before ES6, we used callbacks to handle asynchronous tasks such as network requests.
A Promise is an object that holds the future value of an async operation. For example, if we are requesting some data from a server, the promise promises us to get that data which we can use in the future.
Using promises, we can avoid the infamous ‘callback hell’ and make our code cleaner, easier to read, and easier to understand.
Suppose we want to get some data from a server asynchronously, using callbacks we would do something like this:
getData(function(x){
console.log(x);
getMoreData(x, function(y){
console.log(y);
getSomeMoreData(y, function(z){
console.log(z);
});
});
});
Here I am requesting some data from the server by calling the getData() function, which receives the data inside the callback function. Inside the callback function, I am requesting some more data by calling the getMoreData() function passing the previously received data as an argument, and so on.
This is what we call callback hell, where each callback is nested inside another callback, and each inner callback is dependent on its parent.We can rewrite the above snippet using promises.
Example:
getData()
.then((x) => {
console.log(x);
return getMoreData(x);
})
.then((y) => {
console.log(y);
return getSomeMoreData(y);
})
.then((z) => {
console.log(z);
});
States of Promises:
A Promise in JavaScript just like a promise in real-world has 3 states. It can be
1)unresolved (pending)
2) resolved (fulfilled),
3) rejected.
- Unresolved or Pending — A Promise is pending if the result is not ready. That is, it’s waiting for something to finish (for example, an async operation).
- Resolved or Fulfilled — A Promise is resolved if the result is available. That is, something finished (for example, an async operation) and all went well.
- Rejected — A Promise is rejected if an error happened.
Syntax for Creating promise:
const promise = new Promise((resolve, reject) => {
…
});
We create a new promise using the Promise constructor it takes a single argument, a callback, also known as executor function which takes two callbacks, resolve and reject.
The executor function is immediately executed when a promise is created. The promise is resolved by calling the resolve() and rejected by calling reject().
For example:
const promise = new Promise((resolve, reject) => {
if(allWentWell) {
resolve(‘All things went well!’);
} else {
reject(‘Something went wrong’);
}
});
Promise Chaining:
The then() and catch() methods can also return a new promise which can be handled by chaining another then() at the end of the previous then() method.
We use promise chaining when we want to resolve promises in a sequence.
For example:
const promise1 = new Promise((resolve, reject) => {
resolve(‘Promise1 resolved’);
});
const promise2 = new Promise((resolve, reject) => {
resolve(‘Promise2 resolved’);
});
const promise3 = new Promise((resolve, reject) => {
reject(‘Promise3 rejected’);
});
promise1
.then((data) => {
console.log(data); // Promise1 resolved
return promise2;
})
.then((data) => {
console.log(data); // Promise2 resolved
return promise3;
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log(error); // Promise3 rejected
});
So what’s happening here?
- When promise1 is resolved, the then() method is called which returns promise2.
- The next then() is called when promise2 is resolved which returns promise3.
- Since promise3 is rejected, the next then() is not called instead catch() is called which handles the promise3 rejection.
References: