The Memory Heap: This is where memory is allocated, V8 will store and delete objects and dynamic data based on when they are being used or not.
The Call Stack: This is where the stack frames are located as code executes. It is keeping track of where in the program we are. For example, if a function is called it is put on top of the call stack, once we return from a function it is popped off the call stack.
Although these are the main components when considering the V8 source there are many other aspects, that are separate from the engine but crucial for the runtime, you may be even more familiar within your day to day work:
Web APIs: APIs in your browser, such as the DOM, AJAX, setTimeout, web components, etc.
The Event Loop & The Callback Queue
The Solution: Asynchronous Callbacks
Asynchronous callbacks are functions that are specified to be run at a later time. Let’s take a look at the example below:
The process of the call stack starts with
- pushing the main function (or anonymous function, which lives in the browser)
2. push setTimeout onto the top of the stack
3. pop the setTimeout off the stack
4. push console.log(‘How are you?’) onto the stack
5. pop console.log(‘How are you?’) off the stack
6. pop the main function off the stack, the stack is now empty.
7. push console.log(‘I am good’)
8. pop console.log(‘I am good’)
But what happened after the call stack was empty? How did the last console.log get pushed back on? The answer lies within the event loop:
The Event Loop and Concurrency
When we call setTimeout we pass the callback function (the console.log()) and a delay to the setTimeout call. The setTimeout call is a Web API provided by the browser. So the setTimeout call essentially gets pushed onto a separate “thread” that is provided by the browser, when these “threads” are called they are aware that concurrency has kicked in.
So when the web API is done with its task it pushes the callback function that was passed into it into something called the task queue. The task queue is just a waiting list for callback functions that have yet to run. For these callbacks to get passed back to the stack they need to be sent over by the event loop. The event loop’s job is to look at the stack and look at the task queue, whenever the stack is empty it pops a callback from the task queue onto the stack.
The event loop allows for our code to be non-blocking. The entire stack will always clear before attempting to run anything in the task queue. So if you want to make an asynchronous network call you can do so knowing that your application will render regardless of how long the call takes.
If anything still isn’t quite clicking or if you just want/need more information on the topic you should definitely check out this talk by Philip Roberts and a visualization tool he made to help folks grasp the concept.