0

I have been curious of how js code is executed from beginning to the end.

I have read about the event loop and seen this great video, how stack frames look like here,and also read about how the V8 engine compiles js code here.

Question :

When does V8 starts compiling and executing the code in relation to the event loop stack ?

is it when the function is about to get popped out of the stack?

or do all functions get compiled, right before they are placed on the stack ?

therefore the proccess of putting other function on top is acctually only dealing with machine code, if so does the execution of that machine code occurs when popping the function from the stack ?

In case my question is not understood, i believe via this example it would be better understood

Example :

function foo() {
    var name=`foo`;
    var obj = {
        number: 6
    }
    console.log(obj.number);
}

function baz() {
    var name = `baz`;
    console.log(a);
    foo();
}

baz();
  1. the first process that occurs is lazy parsing, where all the file is being parsed for syntax errors, but not fully parsed so it takes less time.
  2. going through the function declerations

    • does the v8 engine now compiles the function declaration code to machine code ? or its not his turn yet..
  3. baz is called , baz is placed on bottom of the stack ,and in its stack frame the name variable value is stored (since its a primitive).

    • when exactly does buz gets parsed and converted to machine code ? before its placed on the stack ? or when it pops off ?
  4. console.log placed on top of baz and is executed, - console shows baz

    • is this is the place where the console.log js code is compiled to machine code and executed ?
  5. console.logs pops of the stack.

  6. foo is placed on top of baz, obj is placed in heap (since its a reference type), and name=foo is placed in foo`s stack frame.

  7. console.log goes on top of foo, and is executed , console shows 6.

  8. console.log pops off.
  9. foo pops off, along with its local variable value.
  10. baz pops off along with its name=baz local variable
zzzz
  • 161
  • 1
  • 9
  • 1
    The event loop is really nothing more than a queue where the host system places requests to the JS runtime for JS functions to be executed. Those functions are executed only when the JS runtime becomes idle. The implementation details of how items get placed in the event loop are irrelevant to the JS runtime. – Scott Marcus Dec 08 '18 at 20:05
  • Additionally, the functions that are on the event loop will have already been defined and compiled by the JS runtime. In other words, the host is only making requests for JS functions that have already been defined - it's not passing new JS functions that the runtime hasn't seen before. – Scott Marcus Dec 08 '18 at 20:06
  • 1
    I count like 8 questions. That is too broad. I would advise to focus on one question. – trincot Dec 08 '18 at 20:10
  • thanks for comment @ScottMarcus - So after the lazy parsing - js runtime will compile the function code to machine code. only then it is placed on stack , and makes a request to the js runtime to execute the already compiled code ,is it correct ? if so when does this request occur? before the function gets popped of the stack ? , and when does the js runtime becomes idle ? – zzzz Dec 08 '18 at 20:21
  • @ScottMarcus regarding : " The implementation details of how items get placed in the event loop are irrelevant to the JS runtime" , doesnt the event loop places its request to the js runtime according to which item gets placed on the stack ? if not , what triggers the loop to request execution from the run time ? – zzzz Dec 08 '18 at 20:45
  • The event queue items get there from API calls made by the JS runtime. When those API calls complete, and if they have a callback function defined for them, those callbacks are placed on the event loop in the order that the APIs complete. – Scott Marcus Dec 08 '18 at 20:50
  • @ScottMarcus so you say that the function gets compiled, then placed on the stack (when its already machine code) correct ? , but its still unclear to me when does the event loop make requests to the js runtime to execute the code ? it has to be related in the way items get pushed and popped out of the stack – zzzz Dec 08 '18 at 21:10
  • Watch this: https://www.youtube.com/watch?v=NLtL-EEclRc – num8er Dec 08 '18 at 21:16
  • 1
    @num8er , thanks for the nice video , although it is not related to the question.. – zzzz Dec 08 '18 at 22:42
  • 1
    JS code is interpreted. That means that it is compiled on-demand (when it is invoked). This is why you don't know about your errors until you run the code. There is no pre-compilation. When functions are invoked, they get placed on the call stack and when the call-stack has no pending operations, the event loop is consulted to see if any requests for the execution of callback functions have been placed there. It need not be any more complex that this. – Scott Marcus Dec 09 '18 at 01:15
  • 1
    Also, the video that @num8er linked to is very relevant as it discusses callbacks, which is exactly what the event loop is designed for. – Scott Marcus Dec 09 '18 at 01:16
  • @ScottMarcus: "interpreting" and "on-demand compiling" are two different concepts. Most modern JS engines use a combination of both, but you could totally have an engine that does only the one or only the other. – jmrk Dec 10 '18 at 19:37
  • @jmrk In this context, that's a distinction without a difference and it's why I indicated that it's interpreted and compiled on-demand, for the very reason that most implementations do a bit of both. – Scott Marcus Dec 10 '18 at 19:43

1 Answers1

2

There is no such thing as "the event loop stack".

One concept is the "call stack", which is a term for the fact that when functions call each other, they form a stack-like current state of things. This is mostly a theoretical concept, but as it happens, there is indeed an area of memory that is called "the stack" and is used for functions' local variables, but it is not a data structure with a push/pop interface: the act of calling a function places its data on this stack, and returning from the function removes it again, returning control to the calling function.

This answers part of your function: starting to execute a function is literally exactly the same as having this function placed on the call stack. Those are two descriptions for the same thing.

Another concept is the event queue. You can think of it as a queue of functions waiting to be executed; whenever no other function is executing, the next function from this queue is called. Putting a function into the queue does not require it to have been parsed or compiled. In your example snippet, the event queue is not used at all.

Compiling functions is really unrelated to all this. When a function is called (by another function, or by the event loop), it has to be executable in some form -- but depending on your JavaScript engine, it could get interpreted without any compilation, or it could get compiled to bytecode, or it could get compiled to machine code, or the engine could use this opportunity to switch from one to the other.

Since you asked about V8 specifically: in current versions, when V8 sees a function definition like function f() { ... }, it doesn't do anything anything yet (except for a few cases where V8 guesses that the function will be executed soon, in which case it creates bytecode for it immediately). If the function gets queued as a callback, still no parsing or compilation happens. When a function is called for the first time, V8 creates bytecode for it. When the function is called again, the bytecode exists already, so no additional work is required. When a function runs hot enough, V8 eventually decides to compile optimized machine code for it, usually on a background thread. Additional calls to it are opportunities for V8 to check whether the background thread is done producing machine code already; if so, then the next call will use that optimized code instead of interpreting the function's bytecode like the earlier calls did. Note that these implementation details can and will change over time.

One more note for clarification:

in its stack frame the name variable value is stored (since its a primitive).

Not quite. The variable itself is stored in the stack frame, but only as a reference. Whether it refers to a primitive value or not doesn't matter; strings and objects are both allocated on the heap. The local variable will be destroyed when the function returns and its stack frame is torn down; the respective object or string on the heap will (eventually, at some indeterminate time) be cleaned up by the garbage collector.

jmrk
  • 34,271
  • 7
  • 59
  • 74
  • this :"This answers part of your function: starting to execute a function is literally exactly the same as having this function placed on the call stack. Those are two descriptions for the same thing." simplified it for me , thanks a lot ! , btw same goes for parsing the JS code to AST ?, and creating execution context? they both are part of the same process that occurs when the function is placed on the call stack/executed ? and what comes first? the execution context or the parse ? – zzzz Dec 10 '18 at 21:53
  • btw regarding this : "Whether it refers to a primitive value or not doesn't matter; strings and objects are both allocated on the heap." , it kind of contrasts with this accepted answer : https://stackoverflow.com/questions/13266616/primitive-value-vs-reference-value/13266769#13266769 , are you sure even primitives store a reference on stack and their value on heap ? – zzzz Dec 10 '18 at 22:00
  • Yes, I'm sure. That answer is wrong. Just because an answer is "accepted" doesn't mean it is correct. -- Parsing the source is part of bytecode generation, creating the execution context is part of calling, the two are (as explained) unrelated and there is no general answer to "which one comes first". – jmrk Dec 10 '18 at 22:34
  • as you said when a function gets invoked it is placed on the call stack, creates execution context, and the code gets executed. as i learned - in the process of execution the first thing that happens is the parsing to AST. and then all the V8 magic of compiling/executing/profiling/optimizing/OSR/gc , - i don`t seem to understand why you say that creating execution context/parsing/compiling are unrelated ? its whole part of the same process.. (if something i wrote here is wrong please correct me) – zzzz Dec 10 '18 at 23:02
  • i understand that all the process of interpretation (parsing/compiling/executing) is not explicitly related to the call stack (although the creation of execution context is) , but still they are both operations that happen in the process of executing JS code , therefore the way is see it they are related.. – zzzz Dec 10 '18 at 23:36
  • 1
    Of course they are "related" in the sense that V8 does all of that. – jmrk Dec 11 '18 at 01:47