This article will be simple and only mentions basics related to promises in JavaScript and why we need them. Be ready and just read this story and enjoy understanding promises. π€.
πImportant to know that:
JavaScript is a non-blocking language. What does this mean? When JavaScript engine starts to run the code, it doesn't implement the statements in the order as you write them π€ "in some cases". So let's see how can this look like.
ππΌ After executing the above code the result will be:
I'm the first statement
I'm the third statement
I'm the second statement
And the reason behind this behavior is the usage of setTimeout function
which is an asynchronous function in JavaScript.
So, what is asynchronous function? π€
Simply, it's a function that may take some time. Thus, JavaScript engine holds the execution of it and runs the rest of the code. After finishing all non-asynchronous statements, it finally executes this postponed asynchronous function.
This is the exact meaning of non-blocking language, this behavior of holding on the execution of some code written at certain order and be able to execute it later.
Problem definitionπ:
Why this non-blocking behavior can be an issue in some cases? What if you need to do some operations ("b" & "c") which depends on another operation "a" that can take some time? π
Now let me introduce an interesting case study π€.
We need to fetch a video from a database, but this operation can take some time due to internet connection or for any other reasons. After receiving the video, we need to trim it. The last thing we need is to display the trimmed video.
So now we have three operations:
a- Request the database to fetch the video.
b- Trim the received video.
c- Display the trimmed video.
As you see, the three operations depend on each other, you can't trim or display the video until you receive the original one.
Let's define the functions to do each single operation:
Hint: we use setTimeout function
inside fetchDB function
to just simulate as if we are fetching data from a database.
It is time to execute these operations:
ππΌ After executing the above code the result will be:
undefined After trim. And here is the final video to display!
Notice that the first word is "undefined". This value is the result of invoked fetchDB function
in statement number 20. But why it is "undefined"? because of the same reason fetchDB function
has setTimeout function
which will take some time. So the engine skips it and in turns it will return nothing, so the value will be undefined.
Since all other functions trimVideo
and displayVideo
depend on the original video so they won't return the expected result.
What is the solution then?
Solution 1 π: Callback functions
To handle this, we can use callback functions. What is a callback function? It's a function that is a parameter of another function.
fn_1
is a simple regular function. What if it takes a parameter? this parameter can be of any type even if it was a function.
How can you invoke fn_1
function? You need to pass the function as an argument.
ππΌ After executing the above code the result will be:
f1
f2
So now returning to our case, how you use callback functions to successfully execute all operations related to the required video?
The trimVideo function
relies on the fetchDB function
. We can rewrite fetchDB function
to take function as an argument (callback function) which will be the trimVideo function
.
In addition, the displayVideo function
relies on the trimVideo function
.Thus, you can rewrite trimVideo function
to take function as an argument (callback function) which will be the displayVideo function
.
The displayVideo function
will still the same as it is the final operation and there is no more operations depends on it.
Now how can we redefine the main function
?
ππΌ After executing the above code the result will be:
Here is the video. After trim. And here is the final video to display!
Finally, we got the expected results. But it seems that this is a complex code, doesn't it? What we did here is when we invoke the functions, each function that takes callback function added complexity to the code. And this is known as callback hell.Β Writing nested callback functions is so hard and complex.
More details on callback hell can be found here.
Are there any alternative solutions to avoid using callback functions and simplify the code writing? Yes, thanks to ES6.
Solution 2 π: Promises
I know you read a lot to reach here, but the reason behind using promises will help you well understand why promises are so valuable.
What is a promise? Promise is a special object in JavaScript, it has two main attributes or properties (state + result) (Remember this for later discussion) . Let's dive deep and enjoy coding with promises.
As shown in the above code, to construct a promise you need to create an instance object from a predefined class Promise
using new
keyword. Writing new Promise()
will create a promise object. But what is the function inside those round brackets?
(resolve, reject)=>{}
is a function which has two arguments (resolve, reject)
, you can rename them anything else, but by convention they're called resolve and reject.
And now what are those resolve and reject? π€ They are two callback functions "but don't worry we won't write here anymore callback functionsπ . We just need to understand what we write while constructing a promise". As you knew before, when you write a function as an argument you can call this function whenever you want inside the parent function.
And this exactly what happens. When should you call these functions?
you should invoke the
resolve function
when you need to return a value if your code has executed in an expected manner.Otherwise, you should invoke the
reject function
to return a value if your code failed or an error occurred.
Knowing that, only one function got invoked. If you try to invoke both functions after each other without any conditions. Only the first one will be invoked either it was the resolve function
or the reject function
.
At the beginning of this section, we say that promise has two main attributes or properties** (state + result)**.
Simply the state can be one of three states:
- pending
- fulfilled
- rejected
While the result is any value of any type which you pass to the resolve
or the reject
functions when you invoke them.
Results depend on the states. if the state was fulfilled, resolve function get invoked with the result. And when the state was rejected, reject function get invoked with the result.
But _what is a pending state? π€ It is the state of the promise which hasnβt been fulfilled nor rejected yet. This state has no results you can deal with.
Ok, you now understand what is promise. The final question, how can it help you?
Any promise object has then
property. then
is just a function that takes another function as an argument.
The interesting here, that the arguments of this callback function is the result you previously sent through resolve
function.
In addition, any promise object has catch
property. catch
is just a function that takes another function as an argument.
The interesting here as well, that the arguments of this callback function is the result you previously sent through reject
function.
then
and catch
will never get invoked until the promise is fulfilled or rejected meaning that the resolve
or the reject
function must be invoked.
The last thing you need to know is that then
function returns implicit promise
by default. Implicit promise
is a promise you didn't completely write, when you return any value 'X' from the callback function of then
function, this value 'X' acts as a resolved result from a hidden resolve
function. Thus, you can write a chain of then
functions.
Returning to our problem, how can we use promises to handle our situation?
JavaScript engine deals with this chain of then
functions as one unit and doesn't execute any of them until the previous then
function resolved. Which means that any code written inside this chain will be executed in the right order as you write and this is what we need.
In addition, the engine will execute those functions in asynchronous way. It won't block any code after then
functions.
ππΌ After executing the above code the result will be:
First statement out of promise
Second statement out of promise
4
Applying these concepts in our operations we can rewrite our code as the following π:
The difference between writing the code using promises and writing the code using the callback functions is so obvious. The code is more clear, simple and understandable now π₯³.
We can enhance the writing of promises and chains in some cases using async/await
keywords. π Write promises chaining using async/await π article will clarify the idea π.
Error handling of promises is a great topic. It will be clarified in another article, just wait for it π₯.
Top comments (3)
Awesome
Amazing
Awesome