javascriptdom-eventssettimeoutuieventeventqueue

setTimeout and UIEvent order


If a bunch of UIEvents are queued up because of some long-running method in a script, and at the end of the method I setTimeout(myfunc, 0), what order will myfunc get called in relative to handlers for the previously queued events? Is it guaranteed to be called after all previously queued events get handled?


Solution

  • Most events in the browser are processed in FIFO order (first in, first out) via a central event queue. Some UI events such as mousemove events are collapsed so you get the latest state, not necessarily all the in-between events.

    So, the question is really when the mouse event gets into the JS event queue. I wasn't really sure how this worked so I built a couple test demos. What I found was that the answer depends. If a UI event is what is hogging the JS Thread, then it looks like the other UI events don't get into the queue in front of the timer, but if the UI event finishes and some other action (not on the UI) hogs the CPU, then the evnets are queued in proper FIFO order. So, it appears that "it depends" (in Chrome which is what I have tested so far).

    I will post links to the demos in a second...

    This demo shows that if the CPU hogging activity is not in response to a button click, then other button clicks that occur before the setTimeout() is schedule to fire will get processed before the setTimeout():

    long
    click
    click
    click
    timeout
    

    But, this demo where the CPU hogging happens in the button click event handler itself shows the opposite. The clicks that happened before the setTimeout() was scheduled to fire did not get processed before it.

    long
    timeout
    click
    click
    click
    

    Now, I ran these both in Firefox and found that in both cases Firefox processes the events in FIFO order (in the order they actually happened). For the second demo above, this is different than Chrome.

    Now, I ran these both in IE and found that IE always processes the setTimeout() before the UI events - different than both Firefox and Chrome.

    So, these tests show three different results in three different browsers.

    Summarizing Results:

    Browser      Hog CPU in Event Handler     Hog CPU after Event Handler
    ---------------------------------------------------------------------
    Chrome 44       timeout first (not FIFO)        FIFO clicks first
    Firefox 39      FIFO clicks first               FIFO clicks first
    IE 11           timeout first (not FIFO)        timeout first (not FIFO)
    

    For reference, here's the test case that hogs the CPU in the button click event handler:

    document.getElementById("test").addEventListener("click", function() {
        log("click");
    });
    
    document.getElementById("long").addEventListener("click", function() {
        log("long");
        setTimeout(function() {
            log("timeout");
        }, 1900)
        // hhog the CPU here before the event handler finishes
        var t = Date.now();
        while (Date.now() - t < 2000) {}
    });
    

    And, here's the test case that hogs the CPU after the button client event handler has finished.

    document.getElementById("test").addEventListener("click", function() {
        log("click");
    });
    
    document.getElementById("long").addEventListener("click", function() {
        log("long");
    
        // schedule a setTimeout
        setTimeout(function() {
            log("timeout");
        }, 1900)
    
        // hog the CPU, but after the UI event has finished
        setTimeout(function() {
            // spin for 2 seconds to hog the JS thread
            var t = Date.now();
            while (Date.now() - t < 2000) {}
        }, 1);
    });