Who’s there? An asynchronous operation. Knock, knock.

JavaScript has strict rules about what operations happen, and in what order. This allows programmers to write instructions which the computer can follow and produce repeatable results. What happens when the computer doesn’t know how much time a certain operation will take? Will the computer just stop and wait for it to happen? Sometimes, yes. What about those times when we want the computer to something else while it is caught up doing something very intensive? JavaScript gives us this power through asynchronous code.

Because JavaScript is a single threaded language it can only do one thing at a time, and because of that if an operation takes too much time the computer could come to a screeching halt and cause the other functionality we want on our website to become unresponsive. By structuring our code into asynchronous operations JavaScript can take advantage of doing important or quick things first and then moving on to tasks that might take more time. How does Javascript do this? First, JavaScript runs straight through everything that can be done in what is called the Main Stack.

This main stack works by continuously adding functions to the top of a call stack. If functions are added within other functions, those are added to the top before they are eventually returned. The stack works on a last in, first out basis, so a function that runs inside another function will return before the outer function does.This will continue all the way down through the application from the top of the file to bottom, reading right to left.

Credit: https://javascript.plainenglish.io/node-call-stack-explained-fd9df1c49d2e

After all of the main stack code is executed, JavaScript is then going to check something called the Event Loop. This acts as a queue where the code within it will run on a first in, first out basis. Within this event loop there are two main components. The microtask queue, and the macrotask queue.

Microtasks come first and at the moment are primarily used with the Promise object. Those objects will eventually resolve or reject and push their callbacks into the main stack. The callback functions within the promise object will then execute. Before a promise is able to resolve or reject it is usually waiting to receive a response from an external source. Who knows how long that operation might take? There’s no way to know until it is completed, and that is why JavaScript has separated the code into separate compartments so as to not interfere with another operation that may be happening.

The implementation of microtasks allows for two important features. First, is easy to read error catching through the use of the reject callback. The other is the ability to determine when data gets changed. Most of the time this is advantageous because it gives the developer extra control over when and how data is updated on the part of an application that the user cannot see, hopefully preventing issues. Once all of the microtasks are all completed the engine will then move on to the macrotask queue to complete the events there.

credit: https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke

The macrotask queue handles things like user events and other asynchronous code that we might not want to disrupt from the flow of an application. The article Event loop: microtasks and macrotasks gives a couple of great examples. The one I feel is most practical for developers would be highlighting different parts of a page based on syntax. That is something processor heavy that the user doesn’t have much control over, and needs to get done to update the webpage.

However, if the user would want to interact with the web page during that time we would want to allow that to occur. How can we ensure that happens? One solution is to break up the task into multiple calls to separate macrotasks which are then triggered after the first one completes. This way if the user decides they want to interact with the page their action will be added to the queue in between the processor heavy task, and still allow them to take action.

The article above also gives a good example through the use of an incrementation calculation, however it also references the use of a “web-worker” which doesn’t have any ability to interact directly with the web page document object so it will not block the user from performing other tasks while a calculation is being run. That web worker use and functionality seems like a good thing to research next.

That separation of tasks also allows developers to add ways that track progress and inform the user that things are happening behind the scenes. Javascript allows developers to give myriad functionality to users. There are many things that take a long time to do and I know when I’m looking at a webpage I want the ability to do other things or at least see the progress a computer is making. By splitting up the functionality and making different parts of our application smaller pieces we can keep user functions going while the computer is doing all of its work.

Other Resources:

Understanding Event Loop, Call Stack, Event & Job Queue in Javascript

Tasks, microtasks, queues, and schedules

Studying to become a software engineer