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

In this article we will dig into how javascript works under the hood, how it executes our asynchronous javascript code, and in what order (Promise vs setTimeout), how it generates stack trace and much more..

As most developers know, that Javascript is single threaded, means, two statement in javascript can not be executed in parallel. Execution happens line by line, which means each javascript statements are synchronous and blocking. But there is a way to run your code asynchronously, if you use setTimeout() function, a Web API given by browser, which makes sure that your code executes after specified time (in millisecond). Example code:

console.log('Message 1');// Print message after 100 millisecond
setTimeout(function() {
console.log('Message 2');
}, 100);
console.log('Message 3');

setTimeout takes a callback function as first parameter, and time in millisecond as second parameter.

After executing above statements, browser will print “Message 1” & “Message 3” first, then it will print “Message 2”. This is where event loop comes in, which makes sure your asynchronous code runs after all the synchronous code is done executing.

Event Loop Visualisation

I have created the structure of Event loop using HTML and CSS. You can check it out on Codepen :

Image for post
Image for post

Stack: This is where all your javascript code gets pushed and executed one by one as the interpreter reads your program, and gets popped out once the execution is done. If your statement is asynchronous: setTimeout, ajax(), promise, or click event, then that code gets forwarded to Event table, this table is responsible for moving your asynchronous code to callback/event queue after specified time.

Heap: This is where all the memory allocation happens for your variables, that you have defined in your program.

Callback Queue: This is where your asynchronous code gets pushed to, and waits for the execution.

Event Loop: Then comes the Event Loop, which keeps running continuously and checks the Main stack, if it has any frames to execute, if not then it checks Callback queue, if Callback queue has codes to execute then it pops the message from it to the Main Stack for the execution.

Job Queue: Apart from Callback Queue, browsers have introduced one more queue which is “Job Queue”, reserved only for new Promise() functionality. So when you use promises in your code, you add .then() method, which is a callback method. These `thenable` methods are added to Job Queue once the promise has returned/resolved, and then gets executed.

Quick Question now: Check these statements for example, can you predict the sequence of output?:

console.log('Message no. 1: Sync');setTimeout(function() {
console.log('Message no. 2: setTimeout');
}, 0);
var promise = new Promise(function(resolve, reject) {
resolve();
});
promise.then(function(resolve) {
console.log('Message no. 3: 1st Promise');
})
.then(function(resolve) {
console.log('Message no. 4: 2nd Promise');
});
console.log('Message no. 5: Sync');

Some of you might answer this:

// Message no. 1: Sync
// Message no. 5: Sync
// Message no. 2: setTimeout
// Message no. 3: 1st Promise
// Message no. 4: 2nd Promise

because setTimeout was pushed to Callback Queue first, then promise was pushed. But this is not the case, the output will be:

// Message no. 1: Sync
// Message no. 5: Sync
// Message no. 3: 1st Promise
// Message no. 4: 2nd Promise
// Message no. 2: setTimeout

All `thenable` callbacks of the promise are called first, then the setTimeout callback is called.

Why?: Job Queue has high priority in executing callbacks, if event loop tick comes to Job Queue, it will execute all the jobs in job queue first until it gets empty, then will move to callback queue.

If you want to dive deep about why promises gets called before setTimeout, then you can checkout this article Task, Microtasks, Queues and Schedules by Jake Archibald. Which explains this really well.

Code execution notes

setTimeout(function() {
console.log('Message 1')
}, 0);
console.log('Message 2');

In above example, the first output will be “Message 2”, then “Message 1”, even though the setTimeout is set to run after 0 millisecond. Once the browser encounters the setTimeout it pops it from Main Stack to Callback Queue, where it waits for Main stack to finish the second console.log, then setTimeout gets back to Main Stack, and runs the first console.log.

[Optional] Error StackTrace

So we have learned, if interpreter encounters a function, then that function gets pushed into Stack, now if this function calls another function, then that function call will get pushed on top of the stack as well, and the chain goes on until a function execution gets completed or returns something, then only it get’s removed from the Stack and context gets return to the function that called the last function, and then execution get continues.

This stack of function call, is what helps browser to give you Stack trace for the error occurred in a particular function. E.g:

function func1 () {
// Accessing undefined variable will throw error
console.log(err);
}
function func2 () {
func1();
}
function func3 () {
func2()
}
// Calling func3, will result in error in func1
func3();
Image for post
Image for post
Stacktrace for the error

As you can see in the error stacktrace, the error occurred in func1 function, which was called at line no. 7 in func2, and then func2 was called in func3 at line no. 11.

Now when will you utilise event loop?

Written by

Front End Engineer @Browserstack. Ruby/Rails, JS/React-Redux-Webpack/Angular/, Python, Node.js. Anime

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store