Understanding Promises in-depth in JS with fetch() part 1 of 2

This is my first article and I am gonna explain Promises in JS. SO LET'S GO!!

Β·

9 min read

Understanding Promises in-depth in JS with fetch() part 1 of 2

Hello Everyone around the world πŸ’œπŸ’œπŸ’œ

I am Dipen Chavda and we are gonna go on a ride to understand the concept of Promises in ES6.

Little overview of what we are going to cover in this article πŸ˜ƒ:

We are going to understand what is the beautiful concept of promises in JS that was introduced in ES6 to solve some problems that we used to face while we were working with callbacks and here we are talking about the infamous CALLBACK HELL. We have some improvements in code readability and error handling when it comes to moving from the callback way of doing things to utilizing promises in JS. Here we are going to understand the whole flow of how promises work in JS with the example of fetch() function in this two part series so buckle up. πŸš€

So let's begin Part 1...

πŸ₯πŸ₯πŸ₯

What are promises and why should we even care about them in the first place? πŸ€”

Promises are a new concept introduced in ES6 it enhances our experience of readability and error handling when we are doing callbacky things. Let's understand this more clearly with the code snippet below.

function printHello() {
  console.log("hello!");
}

// millions of lines code here

setTimeout(printHello(), 1000);
console.log("take me first!");

We know that setTimeout() runs asynchronously and here we don't know after how many seconds will the printHello() will run because we are not the one who is gonna call the function but the inbuilt browser things will decide e.g event loop and different queues that a browser has but more on that in the part 2 but here, for now, we just need to understand that the function printHello() will run after all the lines are executed in the global scope of the program.

Here problem is that we don't know when or will it ever run? that means we don't know what is going on in the background so here comes the concept of promises.

Whenever you trigger something in the background then don't just throw it out there but have it have some sort of consequences in the javascript memory as well. Have it have some sort of consistency between the things going on in the background and somehow keeping track of that in javascript memory as well.

This exact thing is provided by promises in JS, now this sounds a little bit vague let's clear it out as we go along the article.πŸ€”

So in ES6, we got introduced to two-pronged functions, in simple language functions with two legs. hmm... interesting... πŸ€”

To understand the two-pronged nature of the function we are going to take fetch() as our example.

Now fetch() is a label function that we have through the web APIs which provides us the feature of network request but JS does not actually have it in its specifications. The old school way of doing this was xhr but now we mostly use fetch().

fetch() is one of the most powerful words of javascript, this 5 letter word makes the network request, it lets us keep track of what is going on in the background in javascript memory, and fetch() literally talks to the INTERNET. 🀯

Ok now we start from small and at the end, we will see a bigger picture of how all this works so let's do it...

Starting with the following code snippet :
function display(data){
    console.log(data);
}

const futureData = fetch("https://twitter.com/xyz/tweets/1");
futureData.then(display);

console.log("take me first!")

Here in this code snippet, we can already see that there is something returned back on the left end side. Remember the two-pronged nature of the fetch()? well, that's it. ✨✨ Now we have information through futureData of what is going on in the background...

So here whenever we trigger the fetch() function in return we are going to get an object back and that object is nothing but the one and only promise object.

That object would have two properties value(guess what that is gonna hold after fetch executes?) and onfullfilled. onfullfilled will have an empty array([]) as its initial value and onfullfilled property is a hidden(remember this!!) property in that object but this is a very vital property for promises to work.

{value : ...,
 unfulfilled : []
}

Now, this object returned from fetch will be stored in the futureData.

const futureData = fetch("https://twitter.com/xyz/tweets/1");

Now since this is a two-pronged function so now the right-hand side will talk to the browser part which is network request.

Now. let's visualize this bit till here and with doing that we have a look at how global memory looks like at this stage.

photo1-blog1.png

Now, lets see what fetch() is doing in the background, now we have seen what fetch() did in the javascript memory.

fetch() in the background :

fetch() will be making a network request to the URL specified in the argument. In this case, we are making a call to Twitter API.

blog-photo-2.png

At 0 ms the network request was made and by default fetch() makes GET request and if we wanna post something then we have to specify some headers related to the type of the data and we have to provide the data and specify the method property to POST.

Now the question is, all this is alright but since fetch() does not have a callback function so how are we actually gonna call display() to display the data??

While working with callback style with setTimeout we used to pass a function in the arguments so when the timer is over setTimeout will automatically call that function and that is how it will execute but here with a two-pronged function approach things are a little bit(I mean a lot!!) different.

In fact, the result of the fetch() will be a response object but here since we are trying to understand promises so just pretend that we are only getting a string as our output back from Twitter and that will be stored inside the futureData object in the value property.

Now we are having a little bit of idea that the 2 prongs of the fetch() are intimately connected πŸ₯°πŸ₯°.

Let's recap a bit...

We've seen that fetch() will return us the response object and whenever we get the response object let's say tomorrow(well, that would be a very slow network request...) the response object coming from Twitter will go to the value property of the futureData object.

Now, I think this has been too much time and Twitter has not yet responded back now they must be responding back any minute now...⏰⏰⏰

So now we know that the response may be coming any minute now and whenever the response string comes it will be stored in the value property of the futureData object so what we would want is that whenever the value property gets filled we want to automatically run some code on it to use it but still the question is How are we gonna do that?

Bring some music for another important piece that we have been missing out on while we were talking about value property on the futureData object.

🎢🎢🎢

The return of the onFullfilled property of futureData object.

So what happens is that whenever we get a response stored in the value property of the futureData object that the value of the value property will be taken as the argument of the function inside the onfullfilled array and the function inside the onfullfilled property will autorun itself...

As shown in the following image the display() function will have the value of the value property as its argument and it will autorun.

blog-photo-3.png

Showing the call stack as well...😊😊

Wait a minute hold on but when and how did you insert the function display() inside the onfullfilled property??

To answer that another unsung hero of promises that we might look over but without it, we might not have promises as a concept which is .then()...πŸ”₯πŸ”₯

Now if we generally want to push something into an array we would use push() to do that but here remember?! the onfullfilled property is a hidden property in our futureData object.

futureData.onfullfilled.push(display);
// but we cannot do it because onfullfilled is hidden so we cannot access it like that

Here .then() comes in picture... πŸŽ₯πŸŽ₯

Here inside .then() we have to pass the function and that function will automatically run when the value property will have a value that will come from our request from Twitter.

const futureData = fetch("https://twitter.com/xyz/tweets/1");
futureData.then(display);

alert everybody, after a long time of waiting we have finally received our response from Twitter...🀩🀩🀩

blog-photo-4.png

The response comes at 270ms(took a lot of time peopleπŸ˜‰)

Now first that response in this case "hi" gets stored into the value property as discussed earlier and since .then() will push the display() inside the onfullfilled property of the futureData object so now display() will have value of the value property as its argument and it will autorun...

Now is the time to see the order of execution of the statements in the code.

function display(data){
    console.log(data);
}

const futureData = fetch("https://twitter.com/xyz/tweets/1");
futureData.then(display);

console.log("take me first!")

blog-photo-5.png

Since fetch() works asynchronously so it will run after all the code in the global scope is executed.

So when finally display() function gets executed then first the EXECUTION CONTEXT will be created and the function will be pushed to the callstack as shown in the image above.

Our console will look like the following :

blog-photo-6.png

Now finally we have printed hi on the console then now let me show you the bigger picture of what we have been doing in our journey 🌎🌎 to get to this place...

Ready everybody...πŸ₯πŸ₯πŸ₯

blog-photo-7.png

phew.... but we finally got here 😏😏

Conclusion :

We understood that why we need promises and we understood the intimate bond between javascript memory and the browser web features and now because of the consistent bond between both of them now we are a little out of the oblivion than what we were while using callbacks like we used to with setTimeout.

We understood and went through each and every part of the fetch()'s flow and we also understood the .then() and how it helps us attach our function to the onfullfilled property of our promise object.

Well, this was a hell of a topic that I chose to write on my first article, and the decision I made to go in-depth while doing it, so this is my first article so whoever reads it please don't forget to give your feedback and share this with your dev friends if you like what I wrote then tweet about it and let other people know and if you wanna talk to me or anything I am just one DM away Twitter

Β