Hi people! It's nice to have you again, or if it's your first time here, be welcome!
Before all, let's ask you something. Do you work with Javascript? whether working with Frontend (React, Angular, Vue, Svelte...) or on Backend (Node, NestJS...).
If your answer probably is "yes". So I will ask another question. Do know how Javascript works?
If your answer is "no" or "hmmmm... I know the basics". This article is for you.
Let's understand how Javascript works on background, based on v8 engine.
First, we need to understand that Javascript is an interpreted language. Therefore, it is interpreted by broswer and compiled in C++
, applying the concept of Just in Time (JIT) compiled and non blocking i/o
.
JavaScript engines are designed developed the Just In Time (JIT) Compilation model.
Node works using the v8 Javascript engine, created by google, just like Chrome.
So we have differents engines for each browser:
- Chrome: V8
- Firefox: SpiderMonkey
- Safari: JavascriptCore
- Edge: Chakra (but after some Microsoft update the Edge, now is using the V8)
And we need to remember... Javascript is SINGLE THREAD, but in same time we can use the power of its architecture make asynchronous processes. And this is what i will try to explain in this article.
How Javascript, as single thread language, can deal with asynchronous tasks.
First, let's see some examples:
First Code
console.log('foo');
setTimeout(function timeout() {
console.log('bar')
}, 1000)
If your answer was:
foo
bar
Congratulations!! you're right!!
But, let's add some complexity...
Second Code
Try to guess the output of this code:
function foo() {
console.log('foo')
}
function bar() {
setTimeout(function timeout() {
console.log('bar')
}, 2000)
}
console.log('baz')
foo();
bar();
If your answer was:
baz
foo
bar
Congratulations!! you're right!!
Now the last example:
Third code
Try to guess the output of this code:
function foo() {
console.log("foo");
}
setTimeout(function first() {
console.log("bar");
}, 7000);
setTimeout(function second() {
console.log("baz");
}, 4000);
foo();
console.log("xpto");
If your answer was:
foo
xpto
baz
bar
Congratulations!! you're right!!
If you got all the outputs of all the examples right, you should already imagine how javascript works. But if you made a mistake on output, don't worry! I will try to explain in this article how these examples were executed by Javascript.
There are four "entites", that we need to know on Javascript Engines, the main focus will be on V8 Engine:
- CallStack
- WebApis or LIBUV (C++ library) - and this is a big spoiler
- Callback Queue or Event Queue
- Event Loop
Let's talk one by one:
CallStack
Every command that is read on JS is inserted on a stack, fist in, last out (FILO) (that we called CallStack). So all commands like console.log
, function call are pushed on this stack.
WebApis or Libuv
It's where the assyncronous processs happens and stored. To avoid the main thread beeing blocked, process like setTimeout
is pushed to the WebApis and popped after the asynchronous process finished. You can find some articles, call the webapis also calling libuv, because this structure is a lib of C++.
Callback Queue or Event Queue
Both names represents the structure where the callbacks of WebApis are stored after finish the execution.
Event Loop
And the main actor of this show... Event Loop
. He is responsible to monitor the CallStack and Event Queue to execute the asynchronous process. Think the Event Loop as a "for(;;) or while(true)", that will always be monitoring the execution process and allow to we work with asynchronous tasks on Javascript.
The event loop takes precedence over some structures. The event loop will only run the event queue tasks when the callback stask is empty
We can see the model of this architecture on this example:
Now that you have some idea of this concepts, let's see the execution of each example that I showed above.
I will use the app created by Philip Roberts
to demonstrate the execution. See in this video where Philip made a demonstration of Event Loop on JSConf EU.
And you can use the app, accessing:
Demo App: http://latentflip.com/loupe/
So for the first example:
- The
console.log
is pushed and executed on callStack - The
setTimeout
is pushed on WebApis and process the asynchronous process - After the process finished, the callback is pushed on Callback Queue
- The Event Loop will realize that there is an event on Call Back Queue and push to Call Stack.
So for the second example:
- The
console.log('baz')
is pushed and executed on callStack - The function
foo()
is pushed and executed on callStack - The
setTimeout
inside ofbar()
function is pushed on WebApis and process the asynchronous process - After the process finished, the callback is pushed on Callback Queue
- The Event Loop will realize that there is an event on Call Back Queue and push to Call Stack.
So for the third example:
- The
console.log('foo')
is pushed and executed on Call Stack - The first
setTimeout
with 7 seconds is pushed on Web Apis - The second
setTimeout
with 4 seconds is pushed on Web Apis - The
console.log('Baz')
is pushed and executed on Call Stack - The second
setTimeout
finished first and the callback is pushed on Event Queue to be processed. - Event Loop notice the event on Event Queue and push on Call Stack
- Meanwhile the first
setTimeout
finished also and the callback is pushed on Event Queue. - Notice that the Event Loop will not push the callback of the first function, because the Call Stack is not empty.
- After the total execution, the Event Loop push the first callback to the Call Stack to complete.
And now it's clearer, how Javascript engine works? How even being single thread can handle asynchronous processes?
The magic happens because of Event Loop and Event Queue. This architecture makes the Javascript non-blocking i/o.
I hope this article has helped clarify how things work behind the scenes.
That's all folks! Thank you so much for read until here!
Top comments (1)
thanks for this blog i understand