为什么在 Firefox 上 "click" 事件循环完成之前会触发超时?

Why does the timeout fire before the "click" event loop is complete on Firefox?

步骤:

  1. 运行 Firefox 中的代码
  2. 等待 5 秒(重要!)
  3. 按确定或取消
  4. 期望标志 == 0,但它是 10。

var flag;

function myFunction(){
    flag = 0 
    console.info("initial", flag);
    setTimeout(function(){
        flag = 10;
    }, 200);
  
    confirm("conf");
   
    alert("should be 0: "+flag);
}

document.getElementById('button').onclick=myFunction;
   
                          
<button type="button" id="button">Click Me!</button>

什么?您 标志设置为 10。为什么您希望它为 0?

因为与许多浏览器不同,Firefox 可能 允许 JavaScript 线程 运行 其他代码,而 alertprompt,并且显示 confirm 模态,暂停 current 代码。 (有关该句子中 "may" 的更多信息,请参阅答案结尾。)调用 alert 等的任务已暂停,因此该任务中的代码不会继续,但 其他 任务允许运行。

这意味着

  • 计时器可以获得回调

  • Ajax 完成处理程序可以 运行

从来没有JavaScript运行宁并发在两个地方,但Firefox确实让线程运行其他任务,而一个任务是被那些模态暂停。

这是 Firefox 的一个怪癖。我首先是在 Stack Overflow 上了解到它的,thanks to bobince


为什么我说 "may" 允许 JavaScript 线程 运行:它曾经相当可靠(旧版本的 Firefox)。我在 Firefox 29 或 38 上复制它没有遇到任何问题。Firefox 42 似乎降低了它的可能性,但它确实仍然发生。

我希望(尽管我可能是错的)Mozilla 会更改它以与其他浏览器保持一致,因为有人可能会争辩说它违反了 JavaScript 的 运行-to-completion任务的语义,这些语义刚刚被最新的规范加强和阐明。

我在 Firefox 和 Chrome 中都尝试过 this。在 Firefox 中,我得到了 10 的结果。在 Chrome 中,我得到了 0 的结果。可能 Firefox 和 Chrome 在涉及 confirm 函数时具有不同的并发模型。 Firefox 很可能 运行 在单独的线程中设置模态对话框,允许 setTimeout 在它自己的线程中继续 运行(并在 200 毫秒后增加 flag 的值). Chrome 很可能 运行 在与 setTimeout 相同的线程中连接模态对话框,因此阻塞了 setTimeout 的 运行 连接(并且不允许值flag 待递增)。