【问题标题】:Do DOM Events Always Run Single Threaded?DOM 事件是否总是单线程运行?
【发布时间】:2013-09-20 20:27:02
【问题描述】:

所以我最近answered a question 并且 OP 询问我是否可以在我的答案中添加以下关于 DOM 事件的内容:

也许您还可以添加到您的答案中,不仅它们首先执行,而且后续事件会被阻止,直到第一个事件完成。

嗯,可以我补充一下吗?我是否知道 DOM 事件将一次运行一个事件并等待前一个事件完成后再开始下一个事件?

我至少知道在浏览器 JavaScript 中总是如此吗?

到目前为止,要找到一个确凿的答案非常困难,我本以为“是”,但我就是找不到。


澄清:我不是要在处理程序中添加其他异步处理程序,或者调用 setTimeout 或工作程序等。我要问的是是否保证事件处理程序执行的顺序,并且下一个仅开始前一个已完成执行?一个好的答案会引用一个可靠的来源(最好是一个规范)。这里没有关于线程的内容。

【问题讨论】:

  • 取决于事件处理程序的作用。如果它推迟了新的脚本加载,则不必在下一个事件处理程序冒泡之前完成......即使只是一个 setTimeOut 也可以跳顺序。
  • 当您添加脚本时,它也会等待轮到它运行。它不会破坏事件循环模型。如果您使用setTimeout,您就是在故意操纵订单。请记住,setTimeout 的第二个参数是尽力而为;如果您通过1000,则无法保证它会在 1 秒后运行。您会看到它经常出现偏差,尤其是在很长一段时间内。
  • 是的,浏览器应该支持 JavaScript 代码中的单线程模型。 Web Workers 是并发运行代码的唯一方法。但是,我发现 Firefox 对此有一些错误。如果您正在发出 同步 ajax 请求,并且您还在侦听 websocket,那么即使您的 JavaScript 代码当前在 ajax 请求上被阻止,Firefox 也会在消息到达时错误地调用您的 websocket 消息处理程序。发生这种情况时,一切都会变得一团糟。
  • @Bergi 不,(我实际上在我链接的那个问题中引用了这个问题)。我要问的是 - DOM API 是否保证事件处理程序一个接一个地执行,并且仅在最后一个完成后才执行下一个?这与一般的 JS 或这种方式的计时器无关。这是关于 DOM 事件 api。

标签: javascript multithreading dom event-handling dom-events


【解决方案1】:

嗯,这实际上取决于您在处理程序中做什么以及如何定义第一个处理程序结束时刻?

例如,如果您只在 eventHandler1 中执行同步操作,那么您可以确定 eventHandler2 在 eventHandler1 完成之前不会被触发。原因是 javascript 是单线程的。

但是,想象一下这样的场景: 单击 button1 会触发 eventHandler1,它实际上会发出 ajax 请求。在那种情况下,您实际上认为什么是“eventHandler1 的结束”?如果是 ajax 请求返回的那一刻,那么 eventHandler2 肯定会在 eventHandler1 完成之前开始(并且可能结束)执行。

简而言之:只要您执行仅同步操作 > 顺序是有保证的。

从 cmets 添加:

http://www.w3.org/TR/DOM-Level-3-Events/#sync-async 例如,它说:“此虚拟队列中的每个事件都必须延迟,直到前一个事件完成其传播行为或被取消。”


好吧,现在我们回到“我们在谈论什么类型的事件”这个问题?如前所述:如果它是异步事件,则确保不能保证顺序。但最初的困境是关于点击事件,而那个不是异步的,而是同步的。对于同步事件文档明确指出:必须将同步事件(“同步事件”)视为它们在先进先出模型中的虚拟队列中,按时间发生顺序排序,关于其他事件、DOM 中的更改以及用户交互。


是的,没有保证。现在添加 javascript 是单线程的,你不能让它们同时执行。但是,是的,严格来说 DOM - 无法保证之前会发生哪一个。


再添加一条评论...您可能拥有完全相同的 DOM,但从 Java 多线程环境中访问。然后呢?:) 那么您必须实现自己的线程安全异步事件处理,因为您不再像使用 javascript 那样受到单线程环境的“保护”。所以,我看到的结论是 DOM 规范确实要求同步事件是 fifo 实现的。异步事件的执行取决于堆栈/线程实现。在 Javascript 中,这意味着 2 个处理程序不能重叠,但在 Java 中,例如不一定是这个意思。

【讨论】:

  • 我不明白 - 为什么您认为订单有保证?哪里是这么说的?我不是要在处理程序本身中调用其他异步处理程序。
  • 我不这么认为。 w3.org/TR/DOM-Level-3-Events/#sync-async 例如,它说:“这个虚拟队列中的每个事件都必须延迟,直到前一个事件完成其传播行为,或者被取消。”
  • 是的,这是我在链接到这个的问题中链接到的链接。似乎很明显它会在那里,但我在任何地方都找不到它的说明(尤其是使用多个队列让我感到困惑),这可能是我对语言理解得足够好的问题,具体的报价或你如何得出结论真的很有帮助,因为我似乎无法根据规范推断出这一点。至少不适用于异步事件。
  • 好吧,现在我们回到“我们在谈论什么类型的事件”这个问题?如前所述:如果它是异步事件,则确保无法保证顺序。但最初的困境是关于点击事件,而那个不是异步的,而是同步的。对于同步事件文档明确指出:必须将同步事件(“同步事件”)视为在先进先出模型中的虚拟队列中,按时间发生顺序排序,关于其他事件、DOM 中的更改以及用户交互。
  • 是的,不保证。现在添加 javascript 是单线程的,你不能让它们同时执行。但是是的,严格来说 DOM - 无法保证之前会发生哪一个。
【解决方案2】:

Yes and no,所有的 JS 都运行在同一个线程上,除了 web worker,它们不能访问 DOM。 http://dev.opera.com/articles/view/timing-and-synchronization-in-javascript/

这是该页面上的一些相关信息

所有事件处理函数都按顺序执行,并且在处理下一个事件之前,每个事件都被完全处理(包括通过 DOM 冒泡和执行默认操作)。

但是,稍后在该页面上,他们提到

比赛条件

每个窗口(和框架)都有自己的事件队列。 在 Opera 中,每个窗口都有自己的 JavaScript 线程。这包括 iframe 中的窗口。结果是从不同帧启动的事件处理程序可能同时执行。如果这些同步脚本修改了共享数据(如顶部窗口中的属性),我们就有可能出现竞争条件。

所有事件都被放入一个事件队列中,所有事件处理都发生在同一个线程中。以及所有异步回调,例如 XHR 和 setTimeout

还要注意嵌套事件,因为如果您触发一个事件,许多方法将执行并且它们可能会更改全局状态。示例http://jsfiddle.net/rpxZ4/

$('#d1').click(function(){
    alert('before ');
    $('#d2').trigger('click');
    $('#d3').trigger('click'); 
    alert('after ');
});

$('#d2, #d3').click(function() {
    alert('clicked ' +this.id);
});

以下是 Opera 对时间处理的建议

  • 不要有长时间运行的脚本。
  • 不要使用同步 XMLHttpRequest。
  • 不要让从不同框架启动的脚本操纵相同的全局状态。
  • 不要使用警告框进行调试,因为它们可能会彻底改变程序的逻辑。

【讨论】:

  • “链接来...”去哪里了 :(?另外,合成事件是指不受信任的事件吗?
  • 通常合成事件是不受信任的,除非您使用带有 FF 的签名脚本
  • 感谢您的回答。尽管如此,这是一个保证订单的例子,但我想知道一般规则。当有两个 DOM 事件(如 mousedown、load、mouseover 等)时,在什么情况下我可以确定在最后一个事件完成后触发下一个事件,为什么?
  • 所以您要问...是否保证在单击之前会发生鼠标按下?以及模糊事件应该发生在点击事件之前还是之后?或者你是说我的例子并不能证明处理程序不能被中断,这只是一个例子?
  • @BenjaminGruenbaum 这篇文章不是特定于实现的(如 IE 文章),它一直提到其他浏览器。
猜你喜欢
  • 2011-08-06
  • 1970-01-01
  • 2011-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多