skip to Main Content

I have two functions and a button with a click-event attached to it. While deylayedForTwoSeconds is running, button is clicked. The output suggests that click-event is handled asynchronously because "done" was output before "button clicked", but the click event is prioritized over AsyncFun, which suggests that click events are synchronous.

document
  .querySelector("button")
  .addEventListener("click", () => console.log("button clicked"));

function delayedForTwoSeconds() {
  let now = (later = new Date());
  while (later.getSeconds() - now.getSeconds() < 2) {
    later = new Date();
  }
}

function asyncFunc() {
  setTimeout(() => console.log("AsyncFunc"), 0);
}

asyncFunc();
delayedForTwoSeconds();
console.log("done");

// Output: done
//         button clicked
//         AsyncFunc
<button>Click me</button>

3

Answers


  1. A click event registers a function to be executed when a click happens.

    Read here about Javascript’s event loop: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop

    The event loop periodically checks whether an event happened and when such event happens – such as a click in this case – then the handler(s) of the event are executed.

    Look at the snippet below:

    function foo(that) {
        let currentDate = new Date();
        while ((new Date() - currentDate) < 5000);
    }
    
    setInterval(function() {
        let f = document.getElementById("foo");
        f.innerText = parseInt(f.innerText) + 1;
    }, 500);
    <input type="button" value="run" onclick="foo(this)">
    <div id="foo">1</div>

    We have a click event that lasts for 5 seconds and the UI is blocked in the meantime. That is, the browser actively executes the click event, there is nothing else to execute it, it’s not some passive waiting, but a blocking event. The actual blocking is usually seamless, but in the construct above we see that the click event is handled synchronously once it’s triggered.

    Login or Signup to reply.
  2. A busy-wait such as the one you showed will prevent the main UI thread from doing anything else. In most browsers, that includes even rendering the button, much less handling a button click. Never do busy-waits like that (more here).

    click events, and events in general, work like this:

    • The browser puts a task in the JavaScript main task queue to run the event handler when the main thread is next available to do that.
    • When the thread picks up the task from the task queue, it runs it synchronously.

    So in a sense they’re asynchronous (the task is queued and picked up by JavaScript later), but the processing of them is synchronous (once they’ve been picked up).

    In a comment you’ve said:

    I am just using it in conjunction with setTimeOut with zero delay to see how Js prioritizes click events and callbacks in the callback-queue.

    When you call setTimeout, it sets a timer in the browser’s timer mechanism. When that timer expires, the browser queues a task to call the callback. Even if you use a delay of 0, the task to run the timer callback is not synchronously queued; it’s queued later (how much later may vary by browser).

    When you click a button, the browser queues a task to call the callback. When it does that may, again, vary by browser, although I believe they’re pretty closely aligned with each other on this now.

    Both of those are standard tasks (sometimes called macrotasks to differentiate them from microtasks), so they’ll be processed in the order in which they were queued. There is no relative priority between those tasks other than the order in which they were queued. (In constrast, promise settlement callbacks are microtasks, which happen when the current task completes before the next task is processed, even if that task was queued before the microtask was.)

    There may be variation between browsers, though, in terms of when they queue the tasks. But the experiment in the question has too many variables to draw conclusions from, not least when the button is rendered (since the thread is busy-waiting), when the user sees it, how quickly they reach to click it, whether the browser recognizes that click immediately or only later when the main thread is free to do so, and the degree to which console.log gets tangled up in this.

    Login or Signup to reply.
  3. The order in which things happen can sometimes be surprising because it’s a single-threaded language. When you run the delayedForTwoSeconds function, it hogs the spotlight and nothing else can happen until it’s done. That’s why you see "done" printed before anything else. When you click the button, the browser puts that event in a sort of "waiting room," known as the event queue. However, this event gets VIP treatment and is executed before other tasks waiting in the queue, like the setTimeout in asyncFunc. So, even though setTimeout is supposed to be asynchronous, the button click jumps ahead in line because user interactions often get priority.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search