javascript/browser: 事件调度具体发生在什么时候?

javascript/browser: when does event dispatching exactly happen?

SO question & answers and DOM level3 docs 说明 手动 事件在浏览器中 同步 调度。但是,我的问题涉及与用户相关的事件(实际点击),而不是手动触发的事件。

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

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

问题是:


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

在真实事件的上下文中谈论同步毫无意义,因为事件队列仅在当前执行堆栈已清空时才处理,即当有不再需要执行同步代码。这使得相关的事件处理程序 异步 。您在 fiddle.

中提供的点击事件处理程序也是如此

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

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

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

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

您指的是 JavaScript 事件循环。

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

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

In web browsers, messages are added any time an event occurs and there is an event listener attached to it. If there is no listener, the event is lost. So a click on an element with a click event handler will add a message--likewise with any other event.

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

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

JavaScript runtimes contain a message queue which stores a list of messages to be processed and their associated callback functions. These messages are queued in response to external events (such as a mouse being clicked or receiving the response to an HTTP request) given a callback function has been provided. If, for example a user were to click a button and no callback function was provided – no message would have been enqueued.

下面是关于同步处理消息队列项目的部分:

In a loop, the queue is polled for the next message (each poll referred to as a “tick”) and when a message is encountered, the callback for that message is executed.


最后:

Would there be any difference, theoretically, it events were processed asynchronously?

完全正确。如果您期望三个队列的消息在某处记录 123,它可能会以备用顺序记录序列,例如 21, 3.