Asynchronous Javascript in depth.

Nikhil Mohadikar
The Startup
Published in
7 min readMay 28, 2019

--

Coming from the synchronous code execution background like Ruby, I was always confused about how javascript executes code in an asynchronous way by remaining single threaded. This asynchronous code execution is really important and performance oriented way of computation nowadays which is being adopted by many languages. Although some languages still don’t support it, there are third-party libraries like ReactiveX which helps you to implement asynchronous workflows.

Before diving into the javascript asynchronous flow we will see what is the difference between synchronous code execution and asynchronous code execution.

Synchronous code execution.

Here in the above graphical representation, our main thread is executing code lines one after the other. After executing C2 it makes an API call and sits idle till we get a response and do response handling. Here the problem with this approach is that we are not executing C4 in the idle time even if C4 is completely independent of the response.

Idle time = Time taken by external API.

Asynchronous code execution

Here in the above graphical representation, the main thread is executing code lines one by one, after executing C2 it makes an API call and starts executing C4 even there is no response from the server. It simply defines response handling tasks which need to be executed when the response comes. Here it uses the CPU more effectively.

idle time = (Time taken by external API)-(Time taken by C4)

Here it looks like multiple threads are involved in the execution but the response handling part will be executed only when the current thread is free or it is done executing C4. If we get the response before C4 completes execution, then it will be kept on hold in message queue/event queue.

With the help of Event loop and message queue every non-blocking I/O is handled with the help of callbacks. Just take an example of browser events like the click event. We attach a callback function to click event whenever a task needs to be executed on click event. By taking advantage of this non-blocking I/O property of javascript we can utilize CPU in a better way. Let’s see practically how asynchronous code execution happens.

Event loop

Event loop in javascript is responsible to decide which thing should be executed next. Here it uses some data structures to store functions.

1:- Call stack (LIFO)

2:- Message queue (FIFO)

3:- Job queue (FIFO) (Introduced in ES6/2015).

Consider the above scenario. Let’s see the contents of Call stack and Message queue while executing each line.

Line 1

Here function main() is pushed in Call stack and message queue is empty

Line 2

Here inside main() function, console.log(“I’ll have my breakfast”) is pushed in Call stack which will be executed and popped out.

Line 3

Here setTimeout() is pushed in Call stack and executed. Here setTimeout() is setting a timer of 0 milliseconds to execute a task which will be pushed in Message queue when the timer starts.

Here console.log(“I’ll have my lunch”) is pushed in Message queue because it needs to be executed after some time. Even it needs to be executed in 0 milliseconds it is pushed in the Message queue to wait for its turn.

Line 6

Here while things in Message queue wait for their turn to be executed, next line i.e. console.log(“I’ll have my dinner”) is pushed in Call stack and executed and popped out after that.

Line 7

Here main() function is only remaining in Call Stack which will be executed and popped out of the stack.

Here Call stack is empty so event loop will decide to execute things from the Message queue in FIFO way. So here console.log(“I’ll have my lunch”) will be executed at last.

Difference between message queue and job queue.

In previous section you must be thinking about why job queue is not getting used. Are message queue and job queue both same? The answer is no. They are different and have different priority. Job queue is introduced in ES6 because of Promises. So the callbacks for promises are added into Job queue and all other callbacks are added in Message/Event queue. The priority for Job queue is high as compared to Message/Event queue.

Promises

With the introduction of promises in Javascript ES6 (2015), we can easily add asynchronous workflows to our javascript app without going to the callback hell ( http://callbackhell.com ).

Promises in javascript is a way of solving dependencies at the time of API calls etc. Just like a “Promise” we use our day to day life, Javascript promises works the same way.

Before diving into Javascript Promises, we will see the first fundamentals of Javascript Promises.

In the above example, you can see you can create a new Promise using Promise() constructor function, which accepts a callback function which also requires two callback functions as arguments. Here promise1 is a resolved promise and promise2 is a rejected promise. You can simply correlate this example with the API calls which will result in two possibilities mainly success and failure.

Now that you know how to create a promise lets take a look at how to handle the promises.

In the above example, I have deliberately resolved the promise after 10 seconds using setInterval() function and then handled that promise using then() method. then() method accepts two callbacks to be called when the promise is resolved or rejected.

You can also use catch() on promise object like below.

Note:- catch(errorCallback) = then(null, errorCallback).

There are 3 advantages of promises.

1:- Callbacks will only be called when the promise is resolved or rejected and will not be called before that. This makes it an excellent choice and makes your code loosely coupled with other modules.

2:- Multiple callbacks can be added to create a chain of callbacks which will be called in the order they were added.

3:- It gives you a guarantee that you will get the expected callback executed.

My Favourite thing about promises is chaining

Consider the above scenario taken from MDN web docs. Every call to then()/catch() method returns a promise which can be further used to attach more callbacks. All the callbacks will be executed in the order they were chained

Let’s take a real-life example of calling an API using promises and handling the response.

Here we are using fetch function to call an API which will return a promise and handling that promise.

Async functions are the new addition in ES8(2017).

Now async functions come to the rescue. Based on promises here we have async functions which will make developers life happy.

Note:- await keyword only works inside async function.

In the above code snippet, async function myfirstAsyncFunction() is declared using the async keyword. Inside this function, we wait for firstFunction() to resolve or reject using the await keyword. firstFunction() will either resolve in 3 seconds or reject in 4 seconds based on the value of the random number.

In the above code snippet, we call an API using fetch which will return a Promise and the promise is handled using await keyword. A much cleaner syntax and easy to understand as compared to promises.

An async function always returns a Promise no matter what is returned inside the function. Whatever you return other than a promise will be returned as an argument in the resolve() callback.

Why can’t we use Asynchronous programming everywhere?

Now after reading this blog there will be some questions like why do we not use asynchronous programming everywhere and why every language is not implementing this thing? The reality is that we cannot use asynchronous programming everywhere as it can make your code a lot more confusing. At the time of complex computations, we will have to stick with synchronous programming otherwise we will get some unwanted results due to concurrency issues. Asynchronous programming will be required only when you are interacting with a lot of external API’s and doing I/O tasks. Examples are video streaming applications.

--

--