Do you like to cook? I’m not sure about you, but I enjoy cooking. I usually cook by myself, although I’m not opposed to having some help.
Many times you’ll find yourself in a situation where you will need something in the middle of cooking and that damn thing isn’t in your house and you have to ask someone to get it for you while you’re cooking. In the meantime, you keep doing your job!
You may be familiar with this way of doing things — in programming languages — we refer to it as asynchronous.
We often do things asynchronously in programming. That is we initiate something and before even it completes we initiate some other task.
In JavaScript, we rely a lot on asynchronous operations — whose results we don’t have yet but will at some later point.
JavaScript in itself is not asynchronous but it supports asynchronous programming.
ES6 has introduced an idea that makes handling asynchronous tasks easier: Promises.
A promise is an object — a placeholder for the eventual result.
It is not that we could not do the async operation before. We did!
All we had to do is to make use of Callbacks in a certain way! Doing so was a torture when we needed to do a task when our operation failed.
Promises standardized the way to deal with asynchronous events *and *callbacks.
I’ve seen people struggling with promises. This post aims to assist individuals in comprehending this amazing topic. We will endeavour to gain a proper mental model of promises through real-life examples.
The more you struggle to understand, the less you understand any problem. To understand, the mind must be quiet.
— Jiddu Krishnamurti
Since people forget things but remember stories.
Let us understand promises with this little story
I am not yet a master storyteller! However, the preceding scenario contains all of the necessary information to help you comprehend the concepts of Promises.
Let us take the key points from it and apply them to JavaScript World.
We need a standard description of what a Promise is — nothing better than MDN.
Let us see, how this definition relates to our story.
The definition makes a lot more sense now!
In JavaScript, a Promise is just an object. It is a placeholder for a value we do not have right now but will have later. It ensures that we will eventually know the result of an asynchronous operation.
Circumstances do not matter — only your state of being matters — what state of being are you choosing?
A promise is nothing more than an object, and it is usual for objects to retain a state. By modifying the state, we do many complex tasks in programming.
A promise is always in one of the three states:
- pending: which is the initial state — that is neither fulfilled nor rejected.
- fulfilled: meaning that the operation was completed successfully.
- rejected: meaning that the operation failed.
Let us establish a strong mental model by tying the underlying principles to our tale before diving into the code.
How to work with promises?
If we want to use promise in our code, then we must first understand these three fundamental concepts.
- How a to create Promise?
- How to Fulfil or Reject a Promise?
- How to Run a Callback Function — Depending on Whether the Promise Is Resolved or Rejected.
“It is not the answer that enlightens, but the question.”
— Eugene Ionesco
If you understand these three things, dealing with promises will be a breeze.
Let’s take a look at each one individually.
How to create a Promise*?*
We create an instance of a promise using the new keyword with the promise constructor function.
How to fulfill or reject the promise?
The promise constructor function accepts one function as its argument — let us pass in an arrow function (but we could just as easily use a function expression).
This function is called an executor function, it automatically receives two arguments resolve
and reject
.
Keep in mind — resolve and reject both are functions.
-
resolve
is a function which when called changes the status of the promise from pending to fulfilled. -
reject
is a function which when called changes the status of the promise from pending to rejected
It is important to keep in mind you cannot directly mutate the status of a promise — you can call the resolve function to fulfill the promise or the reject function to reject the promise.
Both these functions are typically called after an async operation.
To keep things simple, let’s use setTimeout()
to simulate the time it takes to search for the Tofu. We’ll assume that it takes your pal five minutes to go out and text you back.
We refactored our code to include the setTimeout
function.
If we get the Tofu — we will call resolve
after five minutes, if we can not get the Tofu — we call reject
after five minutes.
This is pretty much how you fulfill or reject a promise.
How to Run a Callback Function — Depending on Whether the Promise Is Resolved or Rejected.
The final step is to understand how to execute callback functions based on a promise’s state change. Remember that callback functions are functions that are supplied as an argument to another function.
Callback function means that we will use that function as an argument to another function.
Let us define two callback functions — onFulfilled
and onFailure
onFulfilled()
is the function to be called if resolve is called after the async operation
onFailure()
is the function to be called if reject is called after the async operation.
Ideally, there would be more code in your callback functions but we simply log into the console and it serves the purpose.
Going back to our analogy, if we found the Tofu then our promise is fulfilled — we want to set up the table to eat. If the couldn’t find the Tofu then our promise is rejected — we have to start cooking the pasta.
You may be wondering how to make use of these two callback functions?
When we create a new promise using the promise constructor function the promise object gives us access to two methods — then()
and catch()
We can call it using promise.then()
and promise.catch()
.
The important bit here is:
- If the status of the promise changes from pending to fulfilled by calling the
resolve
function — the function that is passed tothen()
function will automatically get invoked - If the status of the promise changes from pending to rejected by calling the
reject
function the function that is passed tocatch()
function will automatically get invoked
In our case, we need to pass
-
onFulfilled
function tothen()
-
onFailure
function tocatch()
Both functions are callback functions since they are supplied as arguments to other functions. I’m reminding you of this repeatedly since I’ve seen many individuals get afraid of the term “callback” — there is nothing to fear — they are just regular functions.
Our promise code works as expected. But there is room for improvement!
What if we want to send out some data when resolving or rejecting a promise?
That way inside our callback functions we can make use of the value to do something else.
Well, it turns out that we can do that by passing an argument to resolve or reject,
- for the resolve function, we’ll pass in a string that says
Got the Tofu
- for reject function, we will pass in a string that says
Can’t get Tofu
But how do we access these strings in our callback function?
Well, the great thing about a promise is that it will automatically inject the argument passed to resolve as the argument to the onFulfilled
callback and the argument passed to reject as the argument to the onFailure
callback.
You can see that I’ve included parameters to both these callbacks and simply log them to the console.
We would now see the output Got the Tofu Set up the table
when the promise is fulfilled or if there is an error and hence a rejection we would see Can't get Tofu Cook pasta.
One great example of using promises is fetching data from a server; we promise that we’ll eventually get the data, but there’s always a chance that problems will occur.
In a practical scenario, things will be different.
Your result would be:
- an object(JSON)
- an array,
- or any other data type that your async operation returns.
Essentially this is pretty much is the fundamentals of promises in JavaScript. There are a few more details which we will understand in the next article.
“A promise means everything, but once it gets broken, sorry means nothing”
— anonymous
Summary
- A promise is an object — a placeholder for the eventual result.
- In past we used Callbacks, to perform the async operation
- Using callback was a pain especially when you needed to do error handling.
- Promises standardized the way to deal with asynchronous events and Callbacks.
- A promise is always in one of three states: pending (the first), fulfilled, or rejected.
- The operation was completed was marked as fulfilled. The operation was rejected, which means it failed.
- We create a promise using the new keyword with the promise constructor function.
- You cannot directly mutate the status of a promise — you can call the resolve function to fulfill the promise or the reject function to reject the promise.
- We learned How a Promise is created, How to Fulfil or Reject a Promise, How to Run a Callback Function — Depending on Whether the Promise Is Resolved or Rejected.
Note of Gratitude
I wanted to take this last opportunity to say thank you.
Thank you for being here! I would not be able to do what I do withoutpeople like youwho follow along and take that leap of faith to read my post.
I hope you’ll join me in my future blog post and stick around because I think we have something great here. And I hope that I will be able to help you along in your career for many more years to come!
See you next time. Bye!
Top comments (16)
This is by far the best article I've read on explaining the concept of promises (I'm sure others will agree). 😊
Thank you so much for taking the time to write this. 🙏
Your hand-drawn illustrations and storytelling are fantastic.
You said you're not a master storyteller, Let me tell you:** You certainly are.**
You are exceptional. 🙌
Anyone can make people believe they are intelligent by rambling on about complicated concepts, but the true genius is the one who can explain these concepts to a child. In this article, you have done just that.
I hope to see you among dev.to top writers.
I’m sorry I didn’t read the full post yet, but just from the first few paragraphs and especially the food ingredient analogy is just too good. I had to stop and write this comment.
I “promise” I’ll read the full post. nervous laughter intensifies 😬
so, have you resolved your promise? haha
Haha! yes, I sure did 🙂
Just to add:
When a promise is no longer pending it is considered settled.
Promise.allSettled() - JavaScript | MDN
The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
It's sometimes useful to remember that just because a promise value has settled doesn't mean that it will immediately get processed if something synchronous is currently being processed. One can
then
onto an already settled promise but the processing will still occur asynchronously.Also recursively settling promises is the fastest way to starve the event loop.
It should be pointed out that this isn't actually the same, and may behave differently in certain cases.
Great article ! A well rounded way of describing promises. Question - what did you use / how did you create your illustrations / storyboard. Im looking for something myself for my articles (I'm very poor at drawing).
Thanks Somnath, it's a good explanation of promises and the story illustration is really amazing.
Just a small suggestion. Please try to use little bit space between the main headings. It sometimes looks like the whole thing is too much stuff into a small space which can be irritating for a reader who is going through the whole blog.
Hope this helps ✌ !
Good explanation! Thank you!
One things tho, which website/apps you use to make the hand drawn illustrations with the code like this one
im curious tho
Nice work!! very good.
The title should be: Promises Made Easy. 🎉
Good job!