setTimeout 和 UIEvent 顺序

setTimeout and UIEvent order

如果由于脚本中的一些长运行 方法导致一堆 UIEvent 排队,并且在我 setTimeout(myfunc, 0) 方法的末尾,myfunc 的顺序是什么相对于先前排队事件的处理程序被调用?是否保证在处理完所有先前排队的事件后调用它?

浏览器中的大多数事件都是通过中央事件队列按 FIFO 顺序(先进先出)处理的。某些 UI 事件(例如 mousemove 事件)已折叠,因此您可以获得最新状态,不一定是所有中间事件。

所以,真正的问题是鼠标事件何时进入 JS 事件队列。我不太确定这是如何工作的,所以我构建了几个测试演示。我发现答案取决于。如果 UI 事件占用了 JS 线程,那么看起来其他 UI 事件不会进入计时器前面的队列,但是如果 UI 事件完成并且一些其他操作(不在 UI 上)占用 CPU,然后 evnet 以正确的 FIFO 顺序排队。所以,看起来 "it depends"(在 Chrome 中,这是我到目前为止测试过的)。

稍后我会 post 链接到演示...

This demo 显示如果 CPU 占用 activity 不是响应按钮点击,那么在 setTimeout() 之前发生的其他按钮点击将被安排到fire 将在 setTimeout():

之前得到处理
long
click
click
click
timeout

但是,this demo CPU 占用发生在按钮单击事件处理程序本身中的情况恰恰相反。在 setTimeout() 计划触发之前发生的点击没有在它之前得到处理。

long
timeout
click
click
click

现在,我 运行 这些都在 Firefox 中,发现在这两种情况下,Firefox 都以 FIFO 顺序处理事件(按照它们实际发生的顺序)。对于上面的第二个演示,这与 Chrome.

不同

现在,我 运行 这些都在 IE 中,发现 IE 总是在 UI 事件之前处理 setTimeout() - 不同于 Firefox 和 Chrome。

因此,这些测试在三种不同的浏览器中显示了三种不同的结果。

总结结果:

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)

作为参考,下面是占用按钮单击事件处理程序中 CPU 的测试用例:

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) {}
});

并且,这是在按钮客户端事件处理程序完成后占用 CPU 的测试用例。

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);
});

经过一些测试后,我不得不修改我的答案,因为我的答案不正确。是的,UI 事件确实优先于超时。对于超时,似乎无论设置什么顺序,UI事件都会先发生。