(Pt3): Promises
the second part of this tutorial, we talked about callbacks and how can we use them to achieve asynchronous programming in JavaScript. In this third part, I'll be introducing you to the world of Promises and how they relate to asynchronous JavaScript. In fact, I promise you that by the end of this story you’ll learn about the what, the how, and the why of promises in JavaScript.
Let’s get into it…
Promises: Who Are We?
Despite the fact that promises ain’t a new concept, they have been officially introduced in the ES6 version of JavaScript. They are mainly used to manage asynchronous tasks in your code.
I highly recommend that you read the first part of this tutorial as it explains how asynchronous code works behind the scenes in JavaScript. Just to have a solid foundation to keep up with this topic.
Okay! Now, time for storytelling…Look at the following scenario…
In this scenario, you’ll be the main character, and you’re playing by the rules of the JavaScript engine. Remember,
You can do only one operation at a time.
In the order they appear in the scenario.
You cannot do any type of I/O operations by yourself.
You are productive, you don’t waste any time waiting, and you are always busy doing something.
# The Story…
So, assuming, that one morning, you decided to ask your friend about today’s weather by phone , then drink a cup of coffee— it’s a silly scenario I know, but bear with me for a while…
The first thing you do, you take your phone, then you enter your friend’s phone number, then you press the call button in this order.
As soon as you press the call button, you don’t have any control over how the call is done. There is something happening in the background that you are ignorant about. All you get is a tone telling you that your call is in process…In other words, you’re getting a PROMISE from your phone operator telling you that your call is “pending”, and they are trying to reach out to your friend.
Yet, because you’re playing by the JavaScript engine rules, you are not going to waste your time waiting for them. You just take their promise into consideration and meanwhile move on to do something else you can do. You’re, of course, going to drink your cup of coffee.
Your phone operator is still working on your call…By the end of their work, there would be two possible outcomes,
whether the promise is “fulfilled”, and a friend will pick up the phone, and you can ask them about today’s weather
or the promise is “rejected”, and something wrong will happen, and more likely your phone operator will tell you why.
End of the storytelling…
# The Takeaway…
The same principles apply when it comes to promises in JavaScript. They are a sort of a garuantee that your asyncrhonous code is being processed.
If you hover over the MDN documentation about promises, you will read,
The
Promise
object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
This translates to what I have been trying to illustrate with the storytelling above. But, if we would like to schematise all of this, it would look something like the following,
So, when the promise is initiated in JavaScript (created), its state is “pending”. However, when it finishes executing, its state will be whether “fulfilled” and a value is returned, or “rejected” with a reason (error). A promise is said to be “settled” when it is fulfilled or rejected, but not pending.
Promises: Do You Want Us to Make You a Promise?
Let’s see some code now…
To create a promise in JavaScript, you can use the constructor function with the new keyword. It takes a function as an argument. This function is called the executor function. It always automatically gets invoked (run) when you create a new promise.
If you try the above sample code on your end, you’ll have ‘Execution…’ printed to the console which means that the function is automatically invoked.
resolve and reject are two functions provided by JavaScript for you to use. You can call resolve() in the case of success and reject() in the case of failure.
Let’s see the following,
If you try this, you’ll get the following on your javaScript console…
Notice that in our code we resolved the promise with the value ‘1’. As a result, when we expand the object Promise in the console, we see that it says that the status is “fulfilled” and the result is “1”.
Basically, a promise object in JavaScript holds two main properties,
status: pending (initially) | fulfilled (success) | rejected (failure)
value: undefined (initially) | any value (fulfilled) | reason (rejected)
A promise represent a proxy for a value that we can get at some point in the future. This is why the promise object defines the then() method that is called whenever the promise is resolved or rejected.
As you can see, the then() method accepts two callbacks, the first one to handle the case of success, and the other to handle the case of failure.
Notice, in the example above, that the value (any value or error) is automatically passed to these callbacks by JavaScript.
The output of running the example code would look like this,
Success: 1
Before we end this section, I’d like to share with you this quote from the MDN documentation. They state,
In an ideal world, all asynchronous functions would already return promises.
One example to provide would be the Fetch API that you might have already used before to fetch some data from an external source.
Promises: Why Us?
In the last part of this tutorial, we talked about the “callback hell” issue that is cause by using multiple nested callback structures in your code. We said that this could lead to an increase in the difficulty of reading and understanding your code.
Promises are here to solve this problem as they provide us with a mechanism called “chaining”. It makes our code cleaner and easier to deal with.
To demonstrate this, I just want to take the same example we had in the previous story and replicate it here using promises.
We will get the same output as in the example done using callbacks,
However, in a much cleaner and more readable way…This wouldn’t be possible without the chaining feature of promises. The fact that the then() method always returns a promise is what allows us to apply the chain to arrange the order of execution.
Promises: What If We Fail?
Above, we’ve seen that it is possible to catch any errors via the second callback in the executor function. However, you can alternatively handle errors with the catch() method defined in the promise object.
Let’s take the same previous example, and intentionally reject the promise with an error to catch it and stop the flow of execution in the chain,
We intentionally caused an error at line 4. Therefore, the error is caught by the then() which accepts a callback as an argument to print the error.
The output would look something like this,
Promises: We Want to Confess Something…
Promises are amazing for asynchronous programming in JavaScript, but you know what is better? The async/await syntax for a much more readable and cleaner synchronous code. This is going to be the subject of our next story…
Stay tuned…
Last updated