Javascript event loop clarification - javascript

I keep seeing explanations of the "Javascript Event Loop" (ie: browser JS runtime event loop) that don't seem plausible to me, and I'm hoping someone can provide some authoritative clarification.
My base asssumption is that the JS event loop is just like event loops we've been working with in UI frameworks for decades, something like:
// [... some initialization ...]
// The Event Loop
while (true) {
if (! EventQueue.isEmpty()) {
event = EventQueue.pop_oldest_item();
event.callback(event [or some other kind of args]);
}
// [... defer to other non-JS tasks...]
}
But I keep seeing explanations (see below for examples) like this:
The event loop:
Checks whether the (Javascript) call stack is empty.
Checks whether the callback queue [AKA EventQueue] is empty.
If call stack is empty and callback queue is NOT empty, then:
a. Dequeue oldest callback queue item.
b. Push that callback function onto the call stack (and no mention is made of calling that function.)
Keep looping.
This obviously vaguely follows my assumed model above, but with two key and troubling differences:
A. Why would the event loop need to check that that the JS call stack is empty? Surely every time around the loop the call stack will be in the same state (whether that's completely "empty" is beside the point -- it doesn't need "checking"). Whatever function was called last time around will have returned, restoring the stack. So that part makes no sense.
B. Why would the event loop "push the callback onto the JS stack"? Shouldn't the event loop just call the function, thereby creating a legitimate stack frame, and a way to return from the function, not to mention actually executing the function?
So I would appreciate a clarification that addresses these explanations and why they are actually correct, or bolsters my strong suspicion that they are incorrect.
Example sources of these event loop explanations:
Philip Roberts: What the heck is the event loop anyway? At 14:00
https://youtu.be/8aGhZQkoFbQ?t=839
Typescript High Performance (book) page 83.
What is the Javascript event loop?
http://altitudelabs.com/blog/what-is-the-javascript-event-loop/
Understanding Javascript Function Executions - Call Stack, Event Loop, Tasks & more
https://medium.com/#gaurav.pandvia/understanding-javascript-function-executions-tasks-event-loop-call-stack-more-part-1-5683dea1f5ec

This is my answer to your question:
JavaScript behaves in a single threaded and synchronous manner, so the event callback function will be executed after Global Execution Context pops off the execution stack. All the event will be added into the what called event queue.
After global execution context finish all the execution, JS engine will keep checking if there exist any event inside the event queue. If JS engine sees there is an event, then it will create a new execution context for callback function and push it on the execution stack.
In JS, each time you invoke a function, JS engine will create an execute context, which creates a private scope where anything declared inside of the function can not be directly accessed from outside the current function scope, and pushes on the top of the execution context stack. After the function finish executing, the execution context will be popped off.

Because of the asynchronous nature of some functions that are executed in the call stack (event handlers, setTimeout, http requests) messages (callbacks) from these operations can be added to the message queue at any time. This can happen while the main thread is still running functions in the call stack so the event loop needs to check to see if it is empty or not. Once empty then it will pull the next message from the message queue and throw it into the call stack. This cycle constitutes the essence of the event loop.
I made a video presentation that explains and demonstrates this process:
https://www.youtube.com/watch?v=4xsvn6VUTwQ

Related

How "call stack" and "event loop" interact or work together?

I am little bit confused about the architecture of Node.js
As i know, in browser runtime environment event loop continuously check call stack empty or not, if empty then push callback from callback queue or microtask queue into call stack.
Nodejs Architecure's image
But in nodejs, event loop checks it is blocking operation or not.
My question is that,
Suppose I have a function in which first 10 lines are synchronous code on 11th line there is some asynchronous or blocking function.
Then as I know (please correct me if i'm wrong), function should first go into callstack and run first 10 lines and when callstack reaches on 11th line then it will go some into another queues.
But as i attached an image link in that it is not clear, what is the flow of code execution between callstack & eventloop.
As i know, in browser runtime environment event loop continuously check call stack empty or not, if empty then push callback from callback queue or microtask queue into call stack.
First off, there's no continuously checking the call stack. The event loop itself is at the root of the call stack. So, when user code finishes executing and returns, that return from the last bit of Javascript that was executing goes back to the event loop where it can examine the various queues and determine which event to process next.
My question is that, Suppose I have a function in which first 10 lines are synchronous code on 11th line there is some asynchronous or blocking function. Then as I know (please correct me if i'm wrong), function should first go into callstack and run first 10 lines and when callstack reaches on 11th line then it will go some into another queues.
An asynchronous function does NOT block. It starts some operation and then code just keeps on executing. So, as an example, if you call fs.readFile() or fs.promises.readFile() in nodejs, those function calls initiate the operation and then immediately return and the line of code right after that executes. As this code after your call to .readFile() finishes executing, it will eventually return control back to the event loop where, some time in the future, that .readFile() function will complete and insert an event in an event queue that the event loop can then process.
what is the flow of code execution between callstack & eventloop.
The call stack controls a given chunk of Javascript executing. When a function call is made, the place that execution should continue at when that function returns is pushed into the callstack. It's just a mechanism for handling function calls. Unlike other languages, variables scoped within a function are not put on the stack because those type of scope variables in Javascript can have a different lifetime than the execution of the function. They are garbage collected instead. So, the callstack in Javascript is just return locations from function calls.
Because nodejs runs your Javascript as a single thread, the event loop does not get to execute any waiting events until a given chunk of Javascript finishes executing, the call stack is then empty and control returns back to the event loop where it can then start a new chunk of Javascript executing to process the next event.
So, the callstack facilitates running a chunk of Javascript. Only when the callstack is empty and control returns back to the event loop can the next event waiting to be processed get run. The two do very different things.

Can someone explain JS Event Loop?

So I kind of understand the JS event loop, but still have a few questions. Here is my scenario and a few questions.
So let's say I have these functions:
function1 - reads an absolute huge file.
function2 - console.log("Hey");
function3 - console.log("What's up");
The way I am understanding this, and correct me if I'm wrong, what would happen is that the function1, function2, and function3, would be added to the queue. Then function1 would be added to the call stack followed by the next two functions.
Now the part where I'm confused is because the first function is going to take an extremely long time what happens to it? Does it get pushed somewhere else so that the next two functions are executed? I think the answer to this is that the only way it gets pushed somewhere else so that you can continue running is to make it an asynchronous function. And the way you make it a asynchronous function is either by using a callback function or promises. If this is the case how does it know that this is a asynchronous function? And where does it get pushed to so that the other two functions can be executed since they are relatively simple?
I think I answered the question myself but I keep confusing myself so if somebody could explain in extremely simple terms that would be great and sorry for the extremely stupid question.
Ordinary function calls are not pushed on the event queue, they're just executed synchronously.
Certain built-in functions initiate asynchronous operations. For instance, setTimeout() creates a timer that will execute the function asynchronously at a future time. fetch() starts an AJAX request, and returns a promise that will resolve when the response is received. addEventListener() creates a listener that will call the function when the specified event occurs on an element.
In all these cases, what effectively happens is that the callback function is added to the event queue when the corresponding condition is reached.
When one of these functions is called, it runs to completion. When the function returns, the event loop pulls the next item off the event queue and runs its callback, and so on.
If the event queue is empty, the event loop just idles until something is added. So when you're just starting at a simple web page, nothing may happen until you click on something that has an event listener, then its listener function will run.
In interactive applications like web pages, we try to avoid writing functions that take a long time to run to completion, because it blocks the user interface (other asynchronous actions can't interrupt it). So if you're going to read a large file, you use an API that reads it incrementally, calling an event listener for each block. That will allow other functions to run between processing of each block.
There's nothing specific that identifies asynchronous functions, it's just part of the definition of each function. You can't say that any function that has a callback argument is asynchronous, because functions like Array.forEach() are synchronous. And promises don't make something asychronous -- you can create a promise that resolves synchronously, although there's not usually a point to it (but you might do this as a stub when the caller expects a promise in the general case). The keyword async before a function definition just wraps its return value in a promise, it doesn't actually make it run asynchronously.

How many JS statements get processed per event loop?

Is there a set number of instructions statements that get processed before checking the event queue/per tick/per loop (ways of saying the same thing, I think?)
Is there a set number of instructions that get processed before checking the event queue/per tick/per loop (ways of saying the same thing, I think?)
No, there is not.
In the node.js architecture, when an event is pulled from the event queue, it's tied to a callback. The interpreter calls that callback and that callback runs to completion. Only when it returns and the stack is again empty does it check to see if there is another event in the event queue to run.
So, it has absolutely nothing to do with a number of instructions. node.js runs your Javascript as single-threaded so there is no time slicing between pieces of Javascript which it sounds like your question perhaps was anticipating. Once a callback is called that corresponds to an even in the event queue, that callback runs until it finishes and returns control back to the interpreter.
So, it goes like this:
Pull event from the event queue
Call the Javascript callback associated with that event
Javascript callback runs until completion and then returns from the callback
node.js internals check event queue for next event. If an event is there, go to step 1 and repeat
If no event is there, go to sleep until an event is placed into the event queue.
In reality, this is a bit of a simplification because there are several different types of event queues with a priority order for which one gets to go first, but this describes the general process as it relates to your question.
There is no set number of instructions that get processed before checking the event queue. Each message is run to completion. From the Mozilla documentation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop):
Each message is processed completely before any other message is processed. This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it may be stopped at any point by the runtime system to run some other code in another thread.
A downside of this model is that if a message takes too long to complete, the web application is unable to process user interactions like click or scroll. The browser mitigates this with the "a script is taking too long to run" dialog. A good practice to follow is to make message processing short and if possible cut down one message into several messages.

Does the JS callstack always have at least one frame?

I've recently seen a presentation on the JS event loop which is, frankly, brilliant, but I have a lingering question now about the JS call stack. If you think about the global execution context as, say, main(), is main() never resolved? My reasoning here is that, if it were, then the JS program would be complete, and no callbacks would happen.
--edit
My primary interest here is how the call stack is represented, in relation to the callback queue. If the event loop is said to wait until the call stack is empty before pushing new frames onto the stack, then the loop would be waiting until the program is complete, and callbacks wouldn't have any effect.
I suppose that means that the event loop waits until there is only one frame left (the main execution context), not no frames.
If you think about the global execution context as, say, main()
No. main is not the global execution context, it is the code that initially runs in that context. The global environment is - through closure - still preserved for the outstanding callbacks after your global code ran, it will not get garbage-collected until your program has finished.
is main() never resolved?
Of course it is. main() is the invocation of any global code - basically, when you loaded a script in your webpage and it executes (yes, this can happen multiple times). Or the global program that you tell node to execute. And once that code has finished running, it does indeed get popped of the stack, as you can see in the talks's animation very well. It does not "continue to run", as that would block the event loop.
My reasoning here is that, if it were, then the JS program would be complete, and no callbacks would happen.
No. The script might have been completed, yes, but your program (browser environment, node etc) has not yet. The event loop is still spinning (or, to be exact: just starts spinning).
The event loop will look at the ongoing asynchronous tasks (XHRs, timeouts, file IO etc) and notice there still are some things working. The program does not exit. Once one (or multiple) of those tasks finish, they put their callback in the callback queue, where it gets picked up by the event loop, and processed by executing it on a brand new call stack. When that stack is empty again, the event loop is back in control, and picks up callbacks from the queue until it is empty. Then again, it looks whether there are still ongoing tasks (one of the callbacks might have spawned new ones), and only when there are none, then the program really ends, and the global environment can be trashed.
So yes, there are indeed times in the execution of a javascript program where the callstack is empty, and only the event loop is spinning happily - actually, that's the situation most of the time.
As you said, main() is the global execution context. It's still alive until :
you close the tab if you're in a browser
you kill the process if you're on a server (like node)
the tab/process crashes
So unless one of the use cases above happen, the runtime is still alive, so is the event loop (which will process the task queue).

When is JavaScript synchronous?

I have been under the impression for that JavaScript was always asynchronous. However, I have learned that there are situations where it is not (ie DOM manipulations). Is there a good reference anywhere about when it will be synchronous and when it will be asynchronous? Does jQuery affect this at all?
JavaScript is always synchronous and single-threaded. If you're executing a JavaScript block of code on a page then no other JavaScript on that page will currently be executed.
JavaScript is only asynchronous in the sense that it can make, for example, Ajax calls. The Ajax call will stop executing and other code will be able to execute until the call returns (successfully or otherwise), at which point the callback will run synchronously. No other code will be running at this point. It won't interrupt any other code that's currently running.
JavaScript timers operate with this same kind of callback.
Describing JavaScript as asynchronous is perhaps misleading. It's more accurate to say that JavaScript is synchronous and single-threaded with various callback mechanisms.
jQuery has an option on Ajax calls to make them synchronously (with the async: false option). Beginners might be tempted to use this incorrectly because it allows a more traditional programming model that one might be more used to. The reason it's problematic is that this option will block all JavaScript on the page until it finishes, including all event handlers and timers.
JavaScript is single threaded and has a synchronous execution model. Single threaded means that one command is being executed at a time. Synchronous means one at a time i.e. one line of code is being executed at time in order the code appears. So in JavaScript one thing is happening at a time.
Execution Context
The JavaScript engine interacts with other engines in the browser.
In the JavaScript execution stack there is global context at the bottom and then when we invoke functions the JavaScript engine creates new execution contexts for respective functions. When the called function exits its execution context is popped from the stack, and then next execution context is popped and so on...
For example
function abc()
{
console.log('abc');
}
function xyz()
{
abc()
console.log('xyz');
}
var one = 1;
xyz();
In the above code a global execution context will be created and in this context var one will be stored and its value will be 1... when the xyz() invocation is called then a new execution context will be created and if we had defined any variable in xyz function those variables would be stored in the execution context of xyz(). In the xyz function we invoke abc() and then the abc() execution context is created and put on the execution stack... Now when abc() finishes its context is popped from stack, then the xyz() context is popped from stack and then global context will be popped...
Now about asynchronous callbacks; asynchronous means more than one at a time.
Just like the execution stack there is the Event Queue. When we want to be notified about some event in the JavaScript engine we can listen to that event, and that event is placed on the queue. For example an Ajax request event, or HTTP request event.
Whenever the execution stack is empty, like shown in above code example, the JavaScript engine periodically looks at the event queue and sees if there is any event to be notified about. For example in the queue there were two events, an ajax request and a HTTP request. It also looks to see if there is a function which needs to be run on that event trigger... So the JavaScript engine is notified about the event and knows the respective function to execute on that event... So the JavaScript engine invokes the handler function, in the example case, e.g. AjaxHandler() will be invoked and like always when a function is invoked its execution context is placed on the execution context and now the function execution finishes and the event ajax request is also removed from the event queue... When AjaxHandler() finishes the execution stack is empty so the engine again looks at the event queue and runs the event handler function of HTTP request which was next in queue. It is important to remember that the event queue is processed only when execution stack is empty.
For example see the code below explaining the execution stack and event queue handling by Javascript engine.
function waitfunction() {
var a = 5000 + new Date().getTime();
while (new Date() < a){}
console.log('waitfunction() context will be popped after this line');
}
function clickHandler() {
console.log('click event handler...');
}
document.addEventListener('click', clickHandler);
waitfunction(); //a new context for this function is created and placed on the execution stack
console.log('global context will be popped after this line');
And
<html>
<head>
</head>
<body>
<script src="program.js"></script>
</body>
</html>
Now run the webpage and click on the page, and see the output on console. The output will be
waitfunction() context will be popped after this line
global context will be emptied after this line
click event handler...
The JavaScript engine is running the code synchronously as explained in the execution context portion, the browser is asynchronously putting things in event queue. So the functions which take a very long time to complete can interrupt event handling. Things happening in a browser like events are handled this way by JavaScript, if there is a listener supposed to run, the engine will run it when the execution stack is empty. And events are processed in the order they happen, so the asynchronous part is about what is happening outside the engine i.e. what should the engine do when those outside events happen.
So JavaScript is always synchronous.
JavaScript is single-threaded, and all the time you work on a normal synchronous code-flow execution.
Good examples of the asynchronous behavior that JavaScript can have are events (user interaction, Ajax request results, etc) and timers, basically actions that might happen at any time.
I would recommend you to give a look to the following article:
How JavaScript Timers Work
That article will help you to understand the single-threaded nature of JavaScript and how timers work internally and how asynchronous JavaScript execution works.
To someone who really understands how JS works this question might seem off, however most people who use JS do not have such a deep level of insight (and don't necessarily need it) and to them this is a fairly confusing point, I will try to answer from that perspective.
JS is synchronous in the way its code is executed. each line only runs after the line before it has completed and if that line calls a function after that is complete etc...
The main point of confusion arises from the fact that your browser is able to tell JS to execute more code at anytime (similar to how you can execute more JS code on a page from the console). As an example JS has Callback functions who's purpose is to allow JS to BEHAVE asynchronously so further parts of JS can run while waiting for a JS function that has been executed (I.E. a GET call) to return back an answer, JS will continue to run until the browser has an answer at that point the event loop (browser) will execute the JS code that calls the callback function.
Since the event loop (browser) can input more JS to be executed at any point in that sense JS is asynchronous (the primary things that will cause a browser to input JS code are timeouts, callbacks and events)
I hope this is clear enough to be helpful to somebody.
Definition
The term "asynchronous" can be used in slightly different meanings, resulting in seemingly conflicting answers here, while they are actually not. Wikipedia on Asynchrony has this definition:
Asynchrony, in computer programming, refers to the occurrence of events independent of the main program flow and ways to deal with such events. These may be "outside" events such as the arrival of signals, or actions instigated by a program that take place concurrently with program execution, without the program blocking to wait for results.
non-JavaScript code can queue such "outside" events to some of JavaScript's event queues. But that is as far as it goes.
No Preemption
There is no external interruption of running JavaScript code in order to execute some other JavaScript code in your script. Pieces of JavaScript are executed one after the other, and the order is determined by the order of events in each event queue, and the priority of those queues.
For instance, you can be absolutely sure that no other JavaScript (in the same script) will ever execute while the following piece of code is executing:
let a = [1, 4, 15, 7, 2];
let sum = 0;
for (let i = 0; i < a.length; i++) {
sum += a[i];
}
In other words, there is no preemption in JavaScript. Whatever may be in the event queues, the processing of those events will have to wait until such piece of code has ran to completion. The EcmaScript specification says in section 8.4 Jobs and Jobs Queues:
Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty.
Examples of Asynchrony
As others have already written, there are several situations where asynchrony comes into play in JavaScript, and it always involves an event queue, which can only result in JavaScript execution when there is no other JavaScript code executing:
setTimeout(): the agent (e.g. browser) will put an event in an event queue when the timeout has expired. The monitoring of the time and the placing of the event in the queue happens by non-JavaScript code, and so you could imagine this happens in parallel with the potential execution of some JavaScript code. But the callback provided to setTimeout can only execute when the currently executing JavaScript code has ran to completion and the appropriate event queue is being read.
fetch(): the agent will use OS functions to perform an HTTP request and monitor for any incoming response. Again, this non-JavaScript task may run in parallel with some JavaScript code that is still executing. But the promise resolution procedure, that will resolve the promise returned by fetch(), can only execute when the currently executing JavaScript has ran to completion.
requestAnimationFrame(): the browser's rendering engine (non-JavaScript) will place an event in the JavaScript queue when it is ready to perform a paint operation. When JavaScript event is processed the callback function is executed.
queueMicrotask(): immediately places an event in the microtask queue. The callback will be executed when the call stack is empty and that event is consumed.
There are many more examples, but all these functions are provided by the host environment, not by core EcmaScript. With core EcmaScript you can synchronously place an event in a Promise Job Queue with Promise.resolve().
Language Constructs
EcmaScript provides several language constructs to support the asynchrony pattern, such as yield, async, await. But let there be no mistake: no JavaScript code will be interrupted by an external event. The "interruption" that yield and await seem to provide is just a controlled, predefined way of returning from a function call and restoring its execution context later on, either by JS code (in the case of yield), or the event queue (in the case of await).
DOM event handling
When JavaScript code accesses the DOM API, this may in some cases make the DOM API trigger one or more synchronous notifications. And if your code has an event handler listening to that, it will be called.
This may come across as pre-emptive concurrency, but it is not: it is the JavaScript code that initiates the API call, and thus controls that the API can do some stuff, but this is just like a function call: once your event handler(s) return(s), the DOM API will eventually also return, and the original JavaScript code will continue after the API call it made.
In other cases the DOM API will just dispatch an event in the appropriate event queue, and JavaScript will pick it up once the call stack has been emptied.
See synchronous and asynchronous events
"I have been under the impression for that JavaScript was always
asynchronous"
You can use JavaScript in a synchronous way, or an asynchronous way. In fact JavaScript has really good asynchronous support. For example I might have code that requires a database request. I can then run other code, not dependent on that request, while I wait for that request to complete. This asynchronous coding is supported with promises, async/await, etc. But if you don't need a nice way to handle long waits then just use JS synchronously.
What do we mean by 'asynchronous'. Well it does not mean multi-threaded, but rather describes a non-dependent relationship. Check out this image from this popular answer:
A-Start ------------------------------------------ A-End
| B-Start -----------------------------------------|--- B-End
| | C-Start ------------------- C-End | |
| | | | | |
V V V V V V
1 thread->|<-A-|<--B---|<-C-|-A-|-C-|--A--|-B-|--C-->|---A---->|--B-->|
We see that a single threaded application can have async behavior. The work in function A is not dependent on function B completing, and so while function A began before function B, function A is able to complete at a later time and on the same thread.
So, just because JavaScript executes one command at a time, on a single thread, it does not then follow that JavaScript can only be used as a synchronous language.
"Is there a good reference anywhere about when it will be synchronous and when it will be asynchronous"
I'm wondering if this is the heart of your question. I take it that you mean how do you know if some code you are calling is async or sync. That is, will the rest of your code run off and do something while you wait for some result? Your first check should be the documentation for whichever library you are using. Node methods, for example, have clear names like readFileSync. If the documentation is no good there is a lot of help here on SO. EG:
How to know if a function is async?

Categories