1

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:

enter image description here

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.

mnj
  • 2,539
  • 3
  • 29
  • 58
  • 1
    Why it works in the video and doesn't work for you? It makes sense that the behavior would be browser-specific. I don't think there is any formal standard that dictates whether to accept and queue up clicks while the UI thread is busy... – obe Jun 08 '21 at 18:30
  • Interesting fact. I just tried running the same code under Chromium - there it behaves exactly like on the video! Interesting this though is that ONLY 1 click is registered during the while loop. If I click more times, just one "click event!" will appear at the end of the console. I wonder why is that. – mnj Jun 08 '21 at 18:44
  • 1
    I just installed Edge on mac :) and it worked just like Chrome as well. I wanted to ask are you testing specifically on any site or tab? – Abhishek Jun 08 '21 at 18:51
  • @Abhishek Interesting. I am running it on elementaryOS (Linux). I am using Visual Studio Code with the Live Server extension to "host" the website. – mnj Jun 08 '21 at 18:52
  • 1
    @Loreno Could you try opening a new default tab on edge and see if it works there. – Abhishek Jun 08 '21 at 18:54
  • @Abhishek That worked! In a new tab all clicks are registered during the while loop (and they are console.logged after the loop of course). I still have the original tab open (it was open by the Live Server extension automatically) - there is still behaves "wrong" – mnj Jun 08 '21 at 18:56
  • 1
    Nice, so something to do with the website you are running in VSC may be :) – Abhishek Jun 08 '21 at 18:56
  • 1
    I’m voting to close this question because it turned out to be an issue caused by VS Code's extension. – mnj Jun 08 '21 at 18:58
  • Thanks for your investigation @Abhishek. I was really confused. – mnj Jun 08 '21 at 18:59
  • Sounds good @Loreno, no worries – Abhishek Jun 08 '21 at 18:59

4 Answers4

1

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.

David Culbreth
  • 2,610
  • 16
  • 26
  • Right, I understand it. The main issue I was wondering about is why it used to work differently in 2015 (as we can see on the YT video) – mnj Jun 08 '21 at 18:41
  • 1
    Depending on the browser's specific implementation, there may have been some undefined behavior in there. I'm not sure. Just looking at the code now though, the behavior you described that you saw is exactly what I would expect from that demo code. – David Culbreth Jun 08 '21 at 18:44
  • I just checked the code under Chromium as well - the behavior is exactly like in the video. I would expect that Edge and Chromium will behave pretty much the same, since they share a lot of codebase, but this is one of the differences between them. Edge will not register the click at all during while. – mnj Jun 08 '21 at 18:46
1

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.

Abhishek
  • 5,649
  • 3
  • 23
  • 42
  • Right, under Chromium it also works like that. I edited my question. Under Edge it works differently. – mnj Jun 08 '21 at 18:51
  • I just installed Edge on mac :) and it worked just like Chrome as well. I wanted to ask are you testing specifically on any site or tab? – Abhishek Jun 08 '21 at 18:51
0

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)

marcx
  • 168
  • 1
  • 8
0

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();
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132