Introduction
Do you feel confuse when you read new Promise()
, .then()
, or .catch()
? You are not alone, and I experience the same thing. Let's start with reading the documentation itself.
According to MDN Web docs, "The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value."
Promise Object States
A promise object has three states pending
, fulfilled
, and rejected
. I will use an analogy here. Same as the name, Promise, it is similar to your friend who wants to make a promise to you. After he/she states a promise with you, the only thing that you can do is wait, right? Waiting is the pending
state in JavaScript promise. In the end, you can find out if your friend will fulfill
your promise or reject
to keep his promise.
Understanding the Promise State Flow
When we create a promise object, at first, we will get a pending state. The promise state will be changed to be fulfilled
if the function inside the promise calls the resolve
callback. However, if the function inside promise calls reject
callback, the state will be changed to be rejected
Try to run this code in your browser console to see how the promise state is changed from pending to fulfilled.
const newPromise = new Promise((resolve) => {
setTimeout(() => resolve('Success'), 2000);
});
console.log("[After promise initialization]", newPromise);
setTimeout(() => console.log("[After resolve callback run]", newPromise), 2000);
Promise Object Methods
After we understand the basic concept of promise states, we can move forward with promise methods. We just saw the state was changing from pending
to fulfilled
, but we didn't access the result. That's the reason why we need the promise methods.
If we take a look inside the promise prototype, we can find out that promise has a constructor method, and three prototype methods, which are .then()
, .catch()
, .finally()
. Therefore, whenever you see those methods are being called, you can assume that the variable before the method is a promise object.
Constructor
The promise constructor requires a callback function to be sent as a parameter. The callback function has two function parameters, and the parameter name convention is resolve
and reject
.
resolve
function will cause the state change to be fulfilled
. On the other hand, reject
will change the state to be rejected
. Both functions has one parameter to return the value.
const fulfilledPromise = new Promise((resolve, reject) => {
resolve("Success")
});
const rejectedPromise = new Promise((resolve, reject) => {
reject("Fail")
});
.then()
Alright, the most popular promise method. You probably see this method everywhere. .then()
has two optional parameters that are onFulfilled
and onRejected
. I guess you can understand it easily. The first parameter will handle the result of the promise if the state is fulfilled
, and the second parameter is for handling rejected
state.
// ...
newPromise.then(
(fulfilledResult) => {
console.log(fulfilledResult);
},
(rejectedResult) => {
console.log(rejectedResult);
}
);
-- OR --
// ...
function onFulfilled(result) {
console.log(result);
}
function onRejected(error) {
console.log(error);
}
newPromise.then(onFulfilled, onRejected);
In fact, in my experience, I don't use the second parameter because we have another method to handle the rejected
state, which we will discuss in the next section.
.catch()
With this method, the rejected
state will directly be handled. It's similar like .then()
, but .catch()
is only has one callback function parameter.
newPromise.catch((error) => {
console.log(error);
});
Example of chaining .catch()
with then()
.
// ...
myPromise.then(result => console.log(result))
.catch(error => console.log(error));
.finally()
Finally, it is .finally()
, the last promise object method. .then()
same like .catch
that it only has one callback function.In addition, It will be called when the promise is settled whether the state is fulfilled
or rejected
. However, the .finally()
callback function doesn't have any parameter.
// ..
newPromise.finally(() => {
console.log('Done');
});
.then()
, .catch()
, .finally()
, and Promise constructor return a promise object. That's why you might see this chaining method.
fetch('https://api.zippopotam.us/us/90210')
.then((res) => res.json())
.then((data) => console.log(data))
.catch((error) => console.log(error))
.finally(() => console.log('done'));
Async and Await
In the beginning, I was confused with fetch()
. Why does fetch
always need double .then
like the previous example. After I read the fetch and response.json() documentation meticulously, I realized that those return promise objects as well. That's why we need .then()
There is another way if we don't want to use chaining .then()
. Thanks to async and await. In order to activate await, we must call await inside the async function. This is an example.
async function fetchData() {
const response = await fetch('https://api.zippopotam.us/us/90210');
const data = await response.json();
}
If I translate to our language, await is like waiting for our friend to answer his promise. Therefore, using await we can get the answer before we execute the next line of code.
Try to run the first code snippet in your browser console, and compare it with the second code. The first code will return a promise, but in the second one, you can get the value.
async function fetchData() {
const response = fetch('https://api.zippopotam.us/us/90210');
console.log(response);
const data = response.json();
console.log(data);
}
fetchData();
async function fetchData2() {
const response = await fetch('https://api.zippopotam.us/us/90210');
console.log(response);
const data = await response.json();
console.log(data);
}
fetchData2();
Conclusion
I wish I can understand about promise in detail at first. It will be so helpful to read the chaining method. Other Promise Methods might be helpful in our code, and I might write them in another post. I hope this promise blog could help you to work around with promise.