JavaScript Call Stack, Event Loop and Callbacks

2 min readWeb Development

V8, Chrome's engineheapmemoryallocationcall stackproductmultiplysquare<anonymous>provided by the browser, not V8Web APIssetTimeout · Ajax via XHR · events such as click and scrolltimer fired / XHR resolvedcallback queuefinished callbacks and events wait here, in orderevent loop: when the stack is empty, move the first callback in the queue onto itthe post's setTimeout(0) example, in orderconsole.log("first")runs on the stack→ firstsetTimeout(cb, 0)goes to the Web APItimer 0 ms → queuecb waitsconsole.log("third")runs on the stack→ thirdconsole.trace()runs on the stack→ the tracestack emptyevent loop moves cbonto the stack→ secondoutput: first · third · the trace · secondsetTimeout guarantees a minimum delay, not an exact one; an Ajax callback takes the same path through the queue.
The runtime as the post describes it. V8 holds the call stack and the heap; the browser adds the Web APIs, the callback queue and the event loop. The stack shows the post's synchronous example (square → multiply → product) as console.trace() prints it. Below it, the post's setTimeout(0) example: the timer is handed to the Web API, its callback waits in the queue, and only once the stack is empty does the event loop move it across, which is why 'second' prints last.

Straight getting into what the title says, a call stack is simply a stack that the javascript run-time maintains to go through all the calls defined in the code. The concept is exactly of a stack where the execution starts from the last function entering the stack and goes on till the first function. This is how all the synchronous languages work, but hey, isn't JavaScript a non-blocking single threaded implementation? lets learn in detail about this.

JavaScript Call Stack

V8, Chromes runtime engine comprises of a Stack and a Heap. Heap is used to allocate memory and stack to do the function calling. Apart from these, a browser consists of Web Api's, Event Loop, and a Callback Queue. The figure at the top of this post puts them together; here is a small description -

  • Web Api’s - Calls such as Ajax, SetTimeOut, Events etc. These are not part of the V8 engine, instead browser provides support for this.
  • CallBack Queue - All the async callback functions or events such as Onclick, or Scroll etc are pushed to this queue. (Detail is provided below)
  • Event Loop - This peace has one function to look in the stack and queue and if the stack is empty, push the contents from callback queue to the stack.

Lets understand the Stack with the following code

let square = function(a, b) {
    return multiply(a, b);
}

let multiply = function(a, b) {
    return product(a, b);
}

let product = function(a, b) {
    return a * b;
}

square(10, 10); // 100

Here is the stack trace ( console.trace() )

product
multiply
square
<anonymous>

Pretty simple, isn’t it. Everything is synchronous. Now lets consider an even simpler code -

console.log("first");
setTimeout(function() {
    console.log("second")
}, 0);
console.log("third");
console.trace();

Here is the output -

first
third
console.trace()
second

What? Even though we are doing setTimeout of 0 seconds, still it was executed at the end. So, here comes the async nature of Javascript. SetTimeOut doesn’t belong to the runtime environment and is part of Web Api provided by browser. The Stack gets everything in order, but setTimeOut goes to webapi, the timer runs for 0 seconds and instead of putting it back to the stack, it puts it to the callback queue and the stack carries on executing the next call. As soon as the stack gets empty, Event Loop comes into the picture and takes the first element in Callback Queue and passes it to the stack and hence it gets executed.

Note - setTimeout does not guarantee execution in the given time, instead it guarantees the minimum time to execute.

All the web Api’s work in the same way. Any ajax call made gets resolved by the browser by calling XHR and the callback is passed to the queue, but the program continues execution and as soon as stack is clear, the function is passed to the stack and the execution continues.

Check out this cool talk which explains about the back concepts of javascript.