【问题标题】:javascript/browser: when does event dispatching exactly happen?javascript/浏览器:事件调度何时发生?
【发布时间】:2017-04-01 11:35:52
【问题描述】:

SO question & answersDOM level3 docs 声明 手动 事件在浏览器中同步 调度。然而,我的问题与用户相关的事件(实际点击)有关,而不是手动触发的。

我使用按钮 + onclick 处理程序创建了一个small jsfiddle demo,该处理程序执行一些同步工作 2 秒(阻塞同步等待,足够长的时间让我的眼睛可以看到发生了什么)。打开控制台查看console.logs。

测试。我在按钮上单击了几次。虽然第一次点击(及其同步处理)使按钮被按下(看起来像禁用),但其他点击也会被存储并稍后通过事件循环异步处理。

问题是:

  • 事件分派究竟何时发生?用户事件(例如点击)是否立即推送到 JS 消息队列,或者是否存在中间 Web API 队列……或者换句话说 - 此事件调度与哪个队列相关?
  • 如果发生用户事件(不是手动事件),大多数事件(clickblur 等 - 除了load)是什么意思?鉴于上面的测试,第一次点击会执行同步阻塞回调(我预计在此期间不会发生任何其他事情),无论如何,下一次点击都存储在队列中。所以存储事件无论如何都是并行的(必须是,因为主 javascirpt 线程很忙)。理论上,事件是异步处理的,会有什么区别吗?

slightly modified demo 表明对于给定的点击事件,该事件会冒泡并调用所有相关的事件处理程序,例如事件冒泡是否阻塞,直到发生其他任何事情(在本例中为超时)。但是仍然不清楚为什么事件调度是同步的

【问题讨论】:

  • 你有一个非常原始的“doNothing”实现。
  • 有多少队列以及您是否将它们视为 Web API 的一部分或 JS 引擎的一部分将完全取决于实现。
  • 您所说的“大多数事件在用户事件的情况下同步处理”是什么意思? 所有 JS代码被阻塞。

标签: javascript events asynchronous concurrency event-loop


【解决方案1】:

在真实事件的上下文中谈论 同步 没有什么意义,因为事件队列仅在当前执行堆栈已被清空时才被处理,即当没有更多同步代码可执行。这使得相关的事件处理程序异步。您在小提琴中提供的单击事件处理程序也是如此。

正在运行的代码在运行时会阻止任何其他操作,这并不表明该代码是如何触发的:异步或同步。在这两种情况下,2 秒的繁忙循环将阻塞 2 秒。当我们说代码异步运行时,它只说明了代码是如何/何时被调用的,而不是它是如何运行的。同步和异步代码都以阻塞的方式运行。这有一些例外——例如web workers 在他们自己的线程中运行。

这些“真实”事件被推送到 JS 消息队列中。当然,它们首先从操作系统事件队列中使用,但这是特定于实现的。重要的是它们最终会出现在 JS 消息队列中,以及它们的处理程序。 JavaScript 引擎仅在之前运行的代码ran to completion 时处理事件。因此,这些事件处理程序被异步调用。

在“手动”事件的情况下,即由代码触发的事件,例如 jQuery 的 .trigger() 方法,区分异步和同步是有意义的。在异步情况下,事件将被放入 JS 队列中,当前代码将首先运行完成。在同步的情况下,事件不会被放到 JS 队列中,而是像函数调用一样被执行。因此,事件的处理作为调用堆栈上的附加调用发生,之后您的代码将恢复,就像从正常函数调用返回之后一样:这是同步行为。

这里我忽略了micro tasks的概念,这只是意味着执行异步代码有不同的优先级。

【讨论】:

  • 感谢您的回答,但我仍然不清楚。请看一下w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#sync-async,上面写着Events may be dispatched either synchronously or asynchronously.,然后在这里:w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#event-types-list 你看到的地方,例如clicksync。我确实了解事件循环和运行完成以及所有内容,但我没有让这些用户事件同步。
  • 这显示了单词 synchronousasynchronous 在不同上下文中的不同用法。尽管 JavaScript 已成为标准,但 DOM (w3.org) 的规范原则上并未对围绕 DOM 构建的脚本语言环境做出假设。在 JavaScript 层之下,事件原则上可以并行处理,这意味着多个事件可以以不可预知的顺序进入 JS 消息队列:这就是 DOM 规范所说的异步。这不是该术语在 JavaScript 上下文中的使用方式:它的意思是:不在同一个执行堆栈上下文中。
  • 该死,据我所知,loaderror 是非常不典型的事件,可以在并行上下文中执行(可能是单独的线程),而用户事件在单线程,因此是同步的?奇怪,async 也是单线程的。
  • 我没有看到与线程的连接。 load 事件处理程序只有在任何其他 JS 代码运行完成时才能开始运行。您可以使用繁忙的循环来延迟它。 DOM 规范中异步的含义是事件处理方式的相对顺序没有预先定义。这与事情是否在 JS 中并行运行无关:它们没有。
【解决方案2】:

您指的是 JavaScript 事件循环。

回答您关于为什么调度(也称为处理消息队列中的项目)同步发生的问题:JavaScript 是单线程的,因此不可能同时处理 2 条消息。

关于 JS 事件循环的一些额外信息:

在网络浏览器中,任何时候发生事件时都会添加消息,并且 是一个附加到它的事件监听器。如果没有监听器,则 事件丢失。所以点击一个带有点击事件处理程序的元素 将添加一条消息 - 与任何其他事件一样。

取自https://developer.mozilla.org/nl/docs/Web/JavaScript/EventLoop

这里有一个不同的看法,取自 http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/:

JavaScript 运行时包含一个消息队列,其中存储了一个列表 要处理的消息及其相关的回调函数。 这些消息排队以响应外部事件(例如 单击鼠标或接收对 HTTP 请求的响应) 给定一个回调函数已经提供。例如,如果用户 要单击一个按钮,但没有提供回调函数 - 没有 消息会被排队。

这里是关于从消息队列中同步处理项目的部分:

在一个循环中,队列被轮询下一条消息(每个轮询 称为“滴答声”),当遇到消息时, 该消息的回调被执行。


最后:

会有什么不同吗,理论上是异步处理事件?

完全。如果您希望三个队列消息在某处记录123,它可能会以交替顺序记录序列,例如213

【讨论】:

猜你喜欢
  • 2017-12-02
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-30
  • 2011-06-23
  • 2017-02-10
  • 2012-01-01
相关资源
最近更新 更多