Search code examples
javascriptevent-handlingevent-loop

Where in terms of priority are eventlisteners in the event callback?


So in this post , the macrotask queue seems to include movemouse. But then with this code

<html>
<body>
    <button id="but_one" onclick='button_one()'>Button 1</button>
    <button id="but_two" onclick='button_two()'>Button 2</button>
</body>
<script>
    //document.addEventListener('mousemove', e => console.log("mouse move"));

    function button_one() {
        console.log("button one")
    }

    function button_two() {
        console.log("button two before")
        setTimeout(() => console.log("timeout : before sleep"), 0)
        autoResolve().then(msg => console.log(".then before sleep " + msg));
        sleep(5000);
        autoResolve().then(msg => {
            sleep(2000);
            console.log(".then after sleep " + msg)
        });
        setTimeout(() => console.log("timeout : after sleep"), 0)
        console.log("button two after")
    }

    function sleep(milliseconds) {
        const date = Date.now();
        let currentDate = null;
        do {
            currentDate = Date.now();
        } while (currentDate - date < milliseconds);
    }

    async function autoResolve() { return "resolved" }
</script>
</html>

If you click button 2 and then 1, you can see that both promises (microtasks) are executed before button 1 click is registered, which makes sense to me. However both timeouts (macrotasks) seem to occur after, even the one that was queued before I clicked. This to me suggests that the listener has it's own 3rd queue but no source says this.

The same is observed with logging mousemove, however I removed that for the purposes of the console in the code-snippet.

Why does this happen?

edit: So this was done in Chrome Version 83.0.4103.61 on Windows 10 PC


Solution

  • The specification says that the event loop may have multiple task queues. Here's step 1 of the processing model:

    1. Let taskQueue be one of the event loop's task queues, chosen in an implementation-defined manner, with the constraint that the chosen task queue must contain at least one runnable task. If there is no such task queue, then jump to the microtasks step below.

    (my emphasis)

    It's not surprising that a browser might (conceptually) have events and timers in separate task queues in order to prioritize an event over a timer callback, perhaps especially when that event has been delayed because the JavaScript thread was too busy to handle it earlier.

    I don't think it's specified any more ...er... specifically than that that "implementation-defined manner." I get the behavior you describe (the "button one" event before the timer callback) with Chrome, and I get the timer callbacks before the event with Firefox.