paint-brush
Asynchronous Javascript For Beginnersby@devanshitank
1,084 reads
1,084 reads

Asynchronous Javascript For Beginners

by Devanshi TankDecember 15th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Asynchronous javascript is the backbone of modern web development. But understanding it comes with its challenges.

Company Mentioned

Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Asynchronous Javascript For Beginners
Devanshi Tank HackerNoon profile picture

Asynchronous javascript is the backbone of modern web development. But understanding it comes with its challenges.

Single threaded languages like Javascript can execute only one thing at a time, which can lead to unnecessary delays and code starvation. Slow functions block further code from executing. Hence, to avoid all this, asynchronous Javascript comes in the picture. It avoids the waiting which can happen due to single threaded execution.

To understand asynchronicity, just the core Javascript engine is not enough. The core JS engine has three main parts

  • Thread of execution
  • Memory/ Variable Environment
  • Callstack

We need to add new components to it like Web browser APIs, Promises, Eventloop, Task queue and Microtask queue.

As we know Javascript runs on the browser. The browser itself is a powerful program, which provides lot of features, some of which makes asynchronous code possible.

These browser features are Dev Tools, Console, Sockets, Network Requests, Rendering(HTML DOM), Timer etc. Javascript has some functions which look like usual functions but are actually a means to interact with these web browsers features. I like to call them 'facade' functions.

Examples of such 'facade' functions are:

  • The document object which refers to the HTML DOM
  • The fetch function which in turn interacts with the Network Requests
  • The setTimeOut() function which refers to the Timer

Let us try to understand this piece of code

function printHello() 
{ 
  console.log('Hello');
}
setTimeOut(printHello, 1000);
console.log('Me First');

Firstly, the function definition of printHello will be saved in the global memory. The javascript facade function that is setTimeOut; the only job it has is to set the Timer to 1000ms and call the function printHello when it is complete.

So once the facade function setTimeOut does that, it moves onto the next line and console logs 'Me First'.

Meanwhile the timer runs and once it finishes, the function printHello is called. The flow of control jumps back to the main Javascript and it prints 'Hello'.

That was a simple code snippet but what about when we need to execute thousands of lines of asynchronous code? We're interfacing with a world outside of Javascript, so we need some rules so that the flow of execution works smoothly.

function printHello()
{
 console.log('Hello');
}
function blockfor1sec()
{ //blocks the thread for one second }
setTimeOut(printHello, 0);
blockfor1sec();
console.log('Me First');

In the above example since the timer is 0ms, one would assume that it runs printHello immediately. But that does not happen. Why?

Javascript has this feature called 'Callback Queue'. The function printHello gets pushed into it, and remains there until JS engine runs all the synchronous code. Another feature the 'Event Loop' keeps a check on the callstack, whether it is empty or not, also if all the global code has finished executing.

Once it is done, the event loop pulls the printHello function from the callback queue and pops it onto the call stack.

console
---------
Me first
Hello

So printHello function executes after the global code. Understanding this removes the fear of unpredictability while writing asynchronous javascript.

There are some problems with this approach though. Suppose the function in setTimeOut is fetching some data from an API and then running another function with that data, we will not be in control of this data. This is because this function is automated by setTimeOut. The data that this function carries is confined within it. The response data is confined with it. This creates a problem. This is made easier to handle with Promises.

Promises

So much of our javascript code which we execute is with the help of browser features, but we have no access to the 'backend' javascript running via browser features. New ES6 feature Promise help us have some sort of consistency between the things going in the background and our actual javascript memory and have some consequence on it. In ES5 we used fetch/xhr to get request on the internet but it didnt impact our actual memory. With the introduction of Promises in ES6, the fetch is going to make a network request whilst also returning an object 'Promise' which will be in our memory. Once the request is finished, the Promise object will be populated with the data from the request.

Promises can be studied by keeping in mind the two pronged approach. The first prong points to the background processes executing via the browser features while the second prong points to the Promise object in our global memory keeping track of the request.

Using these two pronged 'facade' functions helps us

  • initiate background web browser work
  • return a placeholder object(promise) immediately in Javascript.
function display(data)
{
  console.log(data);
}
const futureData = fetch('https//somelink.com');
futureData.then(display);
console.log('Me first');

The above snippet illustrates a simple promise. The value which we get back from the request is stored in futureData and is inturn passed onto as a parameter(data) to display function. To understand how all of this automated, we must understand what does fetch exactly do?

Fetch function is one of most important Javascript function. It promptly executes the two pronged approach which we discussed earlier.

  • It sets up a promise object in our JS memory. The object has two properties: value which stores the response data and onFulfilled which is an empty array.
  • On the other hand, it also initiates browser feature Network Request to send a http request to the domain, get the response data. This is stored in the variable in which the request is stored, that is, in futureData, specifically in the value property of the futureData promise object. futureData.value = response data

We must also inspect what is the purpose of onFulfilled[ ] property onFulfilled is a hidden property of the Promise object, we cant access it. As soon as the value property of the promise object gets some value, onFulfilled property automatically executes the function which was supposed to use the response data. This function is stored in the onFulfilled array. In the above snippet, as soon as the value property gets the response data, the display function is triggered by onFulfilled and the response data is passed as the argument for it.

At this point, certainly the question must arise, how do we add a function to the onFulfilled array? We cant use array.push( ) since it is a hidden property. Instead we use a 'then' method. So that makes it rather simple.
What about error handling in Promises? There might be many instances during which a network request might fail. How does a promise handle failed request and null data? To solve this, a Promise object has one more hidden property on it, onRejection, which is as suspected, an empty array.
We can use the '.catch()' method to add a function to it. This function will be triggered by onRejection and executed in case we get an error.

Lastly we need to know, how does our Promise-deferred functionality gets back into javascript to be executed? There is a feature in javascript similar to callback queue, which is the 'Microtask Queue'. While the network request is being made and data is being fetched, the function associated with the Promise object, is pushed onto the microtask queue.

As soon as the response data is populated, the function is pulled up from the microtask queue and popped onto the callstack by the event loop and executed.

Microtask queue has a higher priority than Callback queue, so the functions associated with other aysnchronous features like setTimeOut( ) are executed after Promise object functions are done executing.

That is a wrap for this post. I hope this article helped and thank you so much for reading.

Happy Coding!