Call Stack

Call stack Below is an example of how code running in the current Call Stack can prevent code on the Event Loop from being executed.

Call Stack

A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions


  • When a script calls a function, the interpreter adds it to the call stack and then starts carrying out the function.

  • Any functions that are called by that function are added to the call stack further up, and run where their calls are reached.

  • When the current function is finished, the interpreter takes it off the stack and resumes execution where it left off in the last code listing.

  • If the stack takes up more space than it was assigned, a "stack overflow" error is thrown.

Given the code

setTimeout(() => { 
  console.log('bye')
}, 2)           
someSlowFn()
console.log('hi')

To start, everything is empty

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  |                   |              | |               |
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

It starts executing the code and pushes that fact onto the Call Stack (here named <global>)

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          |              | |               |
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

setTimeout is pushed onto the Call Stack

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
> setTimeout(() => {  | <global>          |              | |               |
    console.log('bye')| setTimeout        |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

setTimeout triggers the timeout Web API.

Executing setTimeout actually calls out to code that is not part of JS. It's part of a Web API that the browser provides for us. There are different set of APIs like this available in the node.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
> setTimeout(() => {  | <global>          |              | | timeout, 2    |
    console.log('bye')| setTimeout        |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

setTimeout is then finished executing, while the Web API waits for the requested amount of time (2ms).

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          |              | | timeout, 2    |
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

someSlowFn starts executing. Let's pretend this takes around 300ms to complete. For that 300ms, JS can't remove it from the Call Stack

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          | function   <-----timeout, 2    |
    console.log('bye')| someSlowFn        |              | |               |
  }, 2)               |                   |              | |               |
> someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

Still waiting for someSlowFn to finish...

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          | function     | |               |
    console.log('bye')| someSlowFn        |              | |               |
  }, 2)               |                   |              | |               |
> someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

someSlowFn finally finished!

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          | function     | |               |
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
> someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

The next line is executed, pushing console.log onto the Call Stack

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          | function     | |               |
    console.log('bye')| console.log       |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
> console.log('hi')   |                   |              | |               |

We see hi output on the console thanks to console.log

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | <global>          | function     | |               |
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
> console.log('hi')   |                   |              | |               |

Output
> hi

Nothing left to execute, so the special <global> is popped off the Call Stack.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  |                   | function     | |               |
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
> console.log('hi')   |                   |              | |               |

Output
> hi

Whenever the Call Stack is empty, the JS execution environment occasionally checks to see if anything is Queued in the Event Loop. If it is, the first item is moved to the Call Stack for execution.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | function        <---function     | |               
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
> console.log('hi')   |                   |              | |               |

Output
> hi

Executing the function results in console.log being called, also pushed onto the Call Stack.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | function          |              | |               
    console.log('bye')|console.log('bye') |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
> console.log('hi')   |                   |              | |               |

Output
> hi

Once finished executing, bye is printed, and console.log is removed from the Call Stack.

        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  | function          |              | |               |
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

> hi
> bye

Notice that by this point, it is at least 300ms after the code originally requested the setTimeout. Meaning even though we asked for it to be executed after only 2ms, we still had to wait for the Call Stack to empty before the setTimeout code on the Event Loop could be executed

Note: Even if we didn't have someSlowFn, setTimeout is clamped to 4ms as the mimimum delay allowed in some cases

Finally, there are no other commands to execute, so it too is taken off the Call Stack.


        [code]        |   [call stack]    | [Event Loop] | |   [Web APIs]  |
  --------------------|-------------------|--------------| |---------------|
  setTimeout(() => {  |                   |              | |               |
    console.log('bye')|                   |              | |               |
  }, 2)               |                   |              | |               |
  someSlowFn()        |                   |              | |               |
  console.log('hi')   |                   |              | |               |

> hi
> bye

Our program has now finished execution.