This content originally appeared on Envato Tuts+ Tutorials and was authored by Monty Shokeen
This tutorial help familiarize you with Promises in JavaScript. By the end, you will be able to write your own promises and execute callback functions.
Consider the following situation.
A family decides to go on a vacation some time in the future. There are two kids in the family who know that their parents are planning to take them on a vacation. Kids being kids, keep asking the parents every day if they are going on a vacation today. This sounds frustrating.
However, there is a solution. The parents can promise the kids that they will let them know about the vacation as soon as everything is finalized. The kids no longer need to ask the parents about any updates regarding the vacation everyday.
Promises in JavaScript are based on a similar concept. In this tutorial, I will give you a brief overview of Promises in a beginner friendly way.
What is a Promise?
A Promise
in JavaScript is an object that keeps track of the eventual completion or failure of any asynchronous task.
A Promise
can be in three states.
- pending: the promise is neither fulfilled nor rejected. This is the initial state of the promise.
- fulfilled: the task has been completed successfully.
- rejected: the task wasn't completed as expected. Basically, it failed.
Once a promise has either been fulfilled or rejected, it will be considered "settled".
Going back to our vacation analogy. The promise was a family vacation. The parents making arrangements for the vacation was the task that needed to be completed. At present, no one knows if the family would actually go on a vacation. So, the promise is in pending state.
Let's say that the arrangements work out as expected. The promise is now fulfilled. The parents let the kids know and the kids can start planning for the vacation accordingly.
What if the vacation plans couldn't materialize? In this case, the promise is in rejected state. The parents will let the kids know and the kids can now spend their time figuring out what else they could do.
Either way, once they have made the decision, their promise is settled.
How to Create a Promise?
The Promise
constructor in JavaScript accepts an executor function as its parameter. The executor function itself receives two functions as parameters.
The first parameter is the resolving function which is called when you want to resolve the promise. It accepts a single parameter which is the value resolved by the promise. It can either be something primitive like a string or a number. It can also be another promise.
The second parameter is the rejecting function which is called when you want to reject the promise. It also accepts a single parameter which is the reason for rejecting the promise. Usually, it is going to be an instance of the Error
object.
You can name the resolving and rejecting functions whatever you want. In our tutorial, we will call them resolve
and reject
for simplicity.
Let's write a simple fulfilled promise with all these facts in mind:
1 |
let promise = new Promise(function(resolve, reject) { |
2 |
let rand_num = Math.floor(Math.random()*100); |
3 |
setTimeout(() => resolve(rand_num), 1000); |
4 |
});
|
The code inside the executor function that we have passed to the Promise
constructor is executed immediately. We are using the setTimeout()
method to resolve the promise after one second and return the random number.
We can modify the above code slightly to create a promise that gets rejected if the random number is too small and fulfilled otherwise:
1 |
let promise = new Promise(function(resolve, reject) { |
2 |
let rand_num = Math.floor(Math.random()*100); |
3 |
|
4 |
if(rand_num < 20) { |
5 |
setTimeout(() => reject(new Error("Number too small.")), 1000); |
6 |
} else { |
7 |
setTimeout(() => resolve(rand_num), 1000); |
8 |
}
|
9 |
});
|
One thing that I would like to mention here is that the resolve
and reject
functions don't have to be defined by you. The are automatically created by JavaScript when it instantiates the Promise
object. These automatically generated functions are tied to the just instantiated Promise
object.
What to Do With a Settled Promise?
At this point, our promise is well-defined and it either gets resolved or rejected based on the generated random value. However, we are not doing anything with the result of our settled promise.
We obviously did not generate our random number for nothing. How can we use the resulting value for further operations and how can we handle the rejection of our promise when the generated number isn't big enough?
The then()
method of the Promise
object proves useful here. This method accepts two arguments. These two arguments are basically callback functions that are executed based on the resolution or rejection of the promise.
The callback functions in the then()
method are analogous to kids reacting to their parents' vacation plans. The reaction is going to be different depending on whether the parents take the kids on vacation or not.
Let's write the callback functions for our promise:
1 |
let promise = new Promise(function (resolve, reject) { |
2 |
let rand_num = Math.floor(Math.random() * 100); |
3 |
|
4 |
if (rand_num < 20) { |
5 |
setTimeout(() => reject(new Error("Number too small.")), 1000); |
6 |
} else { |
7 |
setTimeout(() => resolve(rand_num), 1000); |
8 |
}
|
9 |
});
|
10 |
|
11 |
promise.then( |
12 |
(result) => console.log(result), |
13 |
(error) => console.error(error.message) |
14 |
);
|
As you can see, we have defined two callback functions for the then()
method. However, only one of them will be called at a time. We will execute the first function and log the result if the promise was resolved. We will execute the second function and log the error if the promise was rejected.
Only the first callback function is actually required when you are using the then()
method. The rejection callback is optional and only needed when you want to handle the rejections.
How to Specifically Handle Promise Rejection?
In the previous section, we learned how to use the then()
method to handle both the fulfillment and rejection of a promise. However, you can also handle just the rejections by using the catch()
method.
Take a look at the following example:
1 |
let promise = new Promise(function (resolve, reject) { |
2 |
let rand_num = Math.floor(Math.random() * 100); |
3 |
|
4 |
if (rand_num < 90) { |
5 |
setTimeout(() => reject(new Error("Number too small.")), 1000); |
6 |
} else { |
7 |
setTimeout(() => resolve(rand_num), 1000); |
8 |
}
|
9 |
});
|
10 |
|
11 |
promise.catch( |
12 |
(error) => console.error(error.message) |
13 |
);
|
In this case, we don't do anything when promise is resolved successfully. We are only logging the errors when the number is too small.
The catch()
method returns a new promise which stays pending when returned. This new promise will get rejected if our callback function throws an error or if it returns a new promise which will ultimately be rejected or fulfilled.
Since the catch(reject)
method only runs when a promise is rejected, it is similar to calling then(null, reject)
.
Executing a Callback Function Whenever a Promise is Settled
In certain situations, you might want to execute some code regardless of the resolution or rejection of a promise. That is the purpose of the finally()
method. This could be important if you want to do cleanups after the executor function of the promise has run, for example. Another advantage of using finally()
is that it prevents code duplication as you no longer have to put the same code inside the resolution and rejection callbacks.
Going back to our vacation analogy, using finally()
is somewhat similar to giving the kids some chocolates regardless of the plans to go on a vacation.
Using the finally(callback)
method is similar to making the call then(callback, callback)
. One difference here is that the callback in finally()
does not receive any arguments. This means that it won't have any idea if the promise was resolved or rejected. All it knows is that the promise was fulfilled.
1 |
let promise = new Promise(function (resolve, reject) { |
2 |
let rand_num = Math.floor(Math.random() * 100); |
3 |
|
4 |
if (rand_num < 50) { |
5 |
setTimeout(() => reject(new Error("Number too small.")), 1000); |
6 |
} else { |
7 |
setTimeout(() => resolve(rand_num), 1000); |
8 |
}
|
9 |
});
|
10 |
|
11 |
promise.then( |
12 |
(result) => console.log(result), |
13 |
(error) => console.error(error.message) |
14 |
).finally( |
15 |
() => console.log("I will always get called!") |
16 |
);
|
Running the above code snippet a couple of times gives me the following output:
1 |
/* First Run
|
2 |
72
|
3 |
I will always get called!
|
4 |
*/
|
5 |
|
6 |
/* Second Run
|
7 |
Number too small.
|
8 |
I will always get called!
|
9 |
*/
|
You might have noticed that we were able to chain finally()
after a call to the then()
method. This is because all three methods then()
, catch()
and finally()
return a Promise
object that allows you to chain other promise methods to the call.
Final Thoughts
In this tutorial, I have tried to cover the basics of the Promise
object in JavaScript in simple terms. You should now be able to understand what promises are and how to write your own promises. You should also be able to deal with different states of a promise and handle its resolution and rejection accordingly.
Promises can be a little bit hard to grasp at first but learning about them is important if you want to execute code asynchronously. We will be publishing many other tutorials related to this topic on Envato Tuts+ soon. Stay tuned!
This content originally appeared on Envato Tuts+ Tutorials and was authored by Monty Shokeen
Monty Shokeen | Sciencx (2023-03-21T00:19:51+00:00) Understanding Promises in JavaScript—The Basics. Retrieved from https://www.scien.cx/2023/03/21/understanding-promises-in-javascript-the-basics/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.