Given the single-threadedness of javascript, when a synchronous event is fired (for DOM manipulation for example) what happens to the currently executing function block when it is interrupted? How does the browser know where/when to continue execution of the interrupted block? Is there some sort of internal addition of a "pointer" to the event loop? I ask because I'm curious whether other waiting events in the event loop can intervene between the intervention of the synchronous event handler and the continuation of the original function block being executed.
And I am very early in my understanding of asynch events/synchronous events/event loop so if I have this totally wrong please let me know. But my understanding is that synchronous events (nested?) are "fired immediately" and I have seen it happen on jsfiddle with standard cut and paste from tutorials on the subject. I'm just confused as to how javascript knows how/where to pick up where it left off since it is so asynch driven.
A snippet:
<script>
var button = document.body.children[0]
var text = document.body.children[1]
button.onclick = function() {
alert('in onclick')
text.focus()
alert('out onclick')
}
text.onfocus = function() {
alert('onfocus')
text.onfocus = null //(*)
}
</script>
will produce 'in onclick', 'onfocus', 'out onclick'. The focus is a synchronous event. How does js know to pick back up with next statement after "text.focus()"? Is it something as simple as the something being done with the frame stack?
All the built-in events are asynchronous, but if you were to fire a synchronous event in your code, it would work like any regular function call that returns back once it's done. No event suddenly interrupts executing code - well pressing "stop execution" in developer tools actually does because the compiled code is littered with checks for that in every function and loop.
Related
I am following https://www.youtube.com/watch?v=Bv_5Zv5c-Ts, and it is explained there that the event loop of JS engine places events in Event Queue - their handlers will be executed only when the Execution Stack is empty. The author even shows an example of it at 1:45:18. The code is:
function waitThreeSeconds() {
var ms = 3000 + new Date().getTime();
while(new Date() < ms) {}
console.log('finished function');
}
function clickHandler() {
console.log('click event!')
}
document.addEventListener('click', clickHandler);
waitThreeSeconds()
console.log('finished execution')
When I run it in the browser, the while loop runs for 3 seconds as expected, and then the 2 messages get printed:
If I click anywhere AFTER the while loop finishes, a message "click event!" gets printed. However, if I click DURING the while loop, the click event is never registered. In the video, in both situations, the click is registered.
Is it due to some updates in the JavaScript engines that happened since the video's premiere in 2015? I'm using the MS Edge browser.
The video's author suggests that even though JS is a single-threaded language, the web browsers implement JS interprets/engines in a concurrent way where the events get added to the Event Queue separately from the JS code's execution.
My experiment confused me since it shows different behavior. Could someone explain why is that?
//EDIT
After further experimentation, I found out that the behavior seen in the video is also to be found in Chromium (and I guess Chrome as well, although I do not have it installed).
Edge, however (the Chromium-based one), behaves differently and does not register the click at all during the while loop.
Web browsers can only operate asynchronously, not technically concurrently. What you've implemented here is what is traditionally called a "busy-wait" loop. This method is a blocking algorithm, meaning that the later code will not execute until the first code is done. This is a good thing though. Imagine how confusing it would be if your code just executed out of order.
If you want to utilize the browser's builtin asynchonous capabilities, you'll need to use one of the functions provided to interact with Javascript's Event Loop.
In this case, you would likely want to use setTimeout() to actually make this properly asynchronous.
function waitThreeSeconds() {
setTimeout(function() {
console.log('finished function');
}, 3000);
}
function clickHandler() {
console.log('click event!')
}
document.addEventListener('click', clickHandler);
waitThreeSeconds()
console.log('finished execution')
Other similar functions are setImmediate(), which executes as soon as the stack is empty, and setInterval(), which allows you to execute a function several times at a regular period or delay.
JavaScript doesn't run concurrently in a browser tab, so whenever you run a for/while loop the thread gets blocked and it must be complete to be able to handle other events in this case your event listeners.
So when you run the while loop no event will get processed until the loop finishes.
I tried running in chrome and it works the same way as it should both the listeners get fired after the loop.
The while loop is blocking, and will prevent the event queue from progressing. There is a similar question here! A good approach is to replace the while loop with a setTimeout(function(){},3000) or setInterval(function(){},3000)
If you use a while loop, you will be blocked from doing anything for those 3 seconds. Instead, you need to use timeout. This will allow you to continue to do things, while the timeout is running.
let activeTimer = null;
function waitThreeSeconds() {
if (activeTimer) {
console.log('stopped function execution');
clearTimeout(activeTimer);
}
console.log('began function execution');
activeTimer = setTimeout(function() {
console.log('finished function function');
activeTimer = null;
}, 3000);
}
function nonBlockingClickHandler() {
console.log(`Click event <${activeTimer != null}>!`);
}
document.addEventListener('click', nonBlockingClickHandler);
waitThreeSeconds();
lets imagine a scenario
function clickEventCheck() {
document.querySelector('#some-id').addEventListener('click', () => {
console.log("The button is clicked");
});
}
clickEventCheck();
Now I get that for the first time the 'clickEventCheck' function is getting called and we can handle the click event.
But now the execution stack is empty so how the event handler line of code which is inside our function(which has returned) still gets executed every time we click the button?
When the function is called click listener is added to the element with id some-id. So, whenever you click the element, the code inside the callback function gets executed.
Until and unless the click listener is not explicitly removed from the element i:e element with id some-id, it will listen to click events.
function clickHandler() {
console.log('The button is clicked');
}
function clickEventCheck() {
document.querySelector('#some-id').addEventListener('click', clickHandler);
}
clickEventCheck();
document.querySelector('.remove').addEventListener('click', () => {
document.querySelector('#some-id').removeEventListener('click', clickHandler);
});
<button id="some-id">Click Me</button>
<button class="remove">Remove Click Listener</button>
JavaScript is doing more than just executing your code sequentially, line by line. In the background there is also something running called the event loop. This frequently checks another stack to see if any other instructions are there and if so runs those, starting additional chains of execution called frames.
Two nice properties of those frames is that they will keep running as long as they can so you don't have to reason about random switching between them, and they don't block, so once a frame can't execute any further, the engine will look for another frame to execute from the stack. This also leads to smooth switching between frames provided you are not doing CPU-heavy execution (which isn't normally what JavaScript's used for).
Practically what might happen is one frame (frame A) makes an I/O request, and provides a callback function when it does so. Frame A then runs out of code to execute and the event loop picks the next frame (frame B) to run. In the background, when the I/O request completes the callback function will be added to the stackāand then when frame B runs out of code, the event loop can pick up that callback and execute it in yet another frame. The same process could apply for a button click, a mouse move, or any asynchronous process you can get the computer to do.
In this way a large number of I/O connections can be smoothly handled simultaneously and this is a big selling point of NodeJs.
A lot of callbacks can get messy quickly and that's why there also exists concepts like Promises and async functions, which are topics for another day.
Edit: I apologize for any confusion. In my code, a loop is running in the script indefinitely until a certain condition is reached. My question is, if an event listener calls a function while this loop is running, after the completion of the function, where would execution continue?
Well, that's pretty much self-explanatory, I guess. After a function is called by an event listener, where does execution of code continue after the function finishes?
There is an event queue, and whatever is next in that queue gets executed. It could for example be a mouse click event, a window resize or a time-out. If there is nothing on the queue, the container, in which JavaScript runs, will just loop until there is something in the queue to process.
You can read more in MDN's article on "Concurrency model and Event Loop":
A JavaScript runtime contains a message queue, which is a list of messages to be processed. To each message is associated a function. When the stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame). The message processing ends when the stack becomes empty again.
A common way to put something on the message queue is by calling
setTimeout(myfunction, 0);
As the delay in the second argument is 0, the function myfunction will be called once the currently executing code completes, i.e. the call stack becomes empty. Note however, that it could be that some other events were already on the queue. In that case those will still be executed first.
What with a long-lasting loop?
You added the following to your question:
In my code, a loop is running in the script indefinitely until a certain condition is reached. My question is, if an event listener calls a function while this loop is running, after the completion of the function, where would execution continue?
If your loop does not run via asynchronous calls (e.g. with repeated calls to setTimeout or with setInterval), but looks like this:
while (!endingCondition) {
// do nothing
}
then there is no way another (JavaScript) event listener gets called. Such an event will be waiting on the message queue. Only when your current loop and any code after it finishes, will that event be processed and result in the call of the event listener.
So let's look at the following example:
var clicked = false;
document.onclick = function() {
clicked = true;
}
while (!clicked) {};
alert('clicked!');
Here one might hope that the while loop gets interrupted by a click and shows the alert, but that is not true. The click event will be in the operating system's message queue, but will not be consumed by JavaScript because it always first completes the code it is currently executing. As stated in the above-mentioned article on MDN:
whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs.
That any code includes all JavaScript code, including code in event handlers.
How to write "interruptible" code
The above example can be made to work like this:
var clicked = false;
document.onclick = function() {
clicked = true;
}
function detectClick() {
if (!clicked) {
setTimeout(detectClick, 0); // keep repeating
return;
}
alert('clicked!');
// do something more ...
};
detectClick(); // call for first time
// We get here immediately. Code will end, and events will be processed.
// One of those "events" is a time-out, to which we have set a handler: detectClick.
// At some point a click event will be there as well, triggering the other handler.
I have a simple resizable div (achieved with jQuery's resizable) and I'm handling the resize event with a function.
So, let's say that's my code:
var a = ...
$(div).resizable({
options go here...
resize: function(e, ui){
make some operations with a
...
...
keep making operations with a
...
...
ok, we're done
}
})
Now, let's say the resize callback is called a lot of times in a short period of time. How will javascript handle that? Will callbacks overlap the use of "a"? (Note that "a" is a global var!).
Am I safe with this code or there might be a conflict of some kind?
Regards
If you're asking about thread safety, JavaScript is single-threaded. A function like that can only be run one at a time, regardless of how often it's called.
So you should be safe.
JavaScript is single-threaded so the function calls can't "overlap". If you trigger the event multiple times then the function is executed entirely (minus any asynchronous functions it may call) in response to the first triggering, before it's executed again to handle the subsequent triggers.
However, changes to a in one call will still have been made when the function is executed again, so if it relies on the value of a you may run into problems - it comes down to exactly what that function is doing with the variable.
I have a function which is an event handler for websocket.onmessage, now since the server can send multiple messages (one after another) and each message will trigger that event, and since the function block may take a few seconds (a lot of rendering going on inside), the function may be called again while the first function call is still running.
I need a critical block in this function in some cases so that the second call will only start the critical section when the first call ends, what's considered a 'best practice' for implementing locks in JavaScript?
Since js is single-threaded, you can't really do locks. Well, you can but you shouldn't.
One idea might be to keep a status variable.
Your function will be called on each onmessage, but you only do something if the variable is set to false. If so, you set it to true and when its done, set it back to false.
var handler; //expose outside the closure
(function(){
var busy = false;
handler = function(){
if( !busy ){
busy = true;
//do rendering stuff
busy = false;
}
}
})();
Obviously, adapt this idea to your own needs.
You could use jQuery Socket https://github.com/flowersinthesand/jquery-socket as it has a callback for the message event.
message(data, [callback])
This means you can get the next message after the first has completed.
EXAMPLE:
websocket.onmessage(data, function(){
//get next message
});
JS is multithreaded only if you use webworkers. I don't know if websockets are even allowed on worker threads because of the lack of synchronization available, but if you set up all your websockets from the main thread, the events will all fire in order on the main thread, so you do not have to perform any blocking or synchronization yourself (see this thread on synchronization in JS)