DOM输入事件与setTimeout/setInterval顺序

Tre*_*ham 12 javascript dom input javascript-events

我的页面上运行了一段JavaScript代码; 我们称之为func1.运行需要几毫秒.当该代码正在运行时,用户可以单击,移动鼠标,输入一些键盘输入等.我有另一个代码块func2,我希望在所有这些排队的输入事件都已解决后运行.也就是说,我想确保订单:

  1. func1
  2. 所有处理程序都绑定到func1正在运行时发生的输入事件
  3. func2

我的问题是:在呼唤setTimeout func2, 0,在年底func1足以保证此排序,在所有现代浏览器?如果那条线在那个开始时出现怎么样func1?在那种情况下我应该期待什么?

请通过参考相关规范或测试用例来备份您的答案.

更新:事实证明,不,这还不够.我在原始问题中没有意识到的是,在执行当前代码块之前,输入事件甚至没有添加到队列中.所以,如果我写

// time-consuming loop...
setTimeout func2, 0
Run Code Online (Sandbox Code Playgroud)

然后,只有setTimeout运行之后,才会将在耗时循环期间发生的任何输入事件(点击等)排队.(为了测试这一点,请注意,如果耗时的循环之后onclick立即删除回调,那么在循环期间发生的单击将不会触发该回调.)因此首先排队并优先.func2

设置超时1似乎可以解决Chrome和Safari中的问题,但在Firefox中,我看到输出事件在超时80(!)之后解决.所以纯粹基于时间的方法显然不会做我想要的.

简单地将一个包裹setTimeout ... 0在另一个内部也不够.(我希望第一次超时会在输入事件排队后触发,第二次超时会在解决之后触发.没有这样的运气.)也没有添加第三或第四级别的嵌套就足够了(参见下面的更新2) ).

所以,如果有人有办法实现我所描述的(除了设置90+毫秒的超时),我将非常感激.或者使用当前的JavaScript事件模型这是不可能的?

这是我最新的JSFiddle测试平台:http://jsfiddle.net/EJNSu/7/

更新2:部分解决方法是嵌套func2在两个超时内,在第一个超时中删除所有输入事件处理程序.但是,这会产生令人遗憾的副作用,导致func1无法解决期间发生的某些甚至所有输入事件.(前往http://jsfiddle.net/EJNSu/10/并尝试快速点击链接几次以观察此行为.警报会告诉您有多少次点击?)所以这再一次让我感到惊讶; 我不认为调用setTimeout func2, 0,func2设置onclicknull,可以阻止回调运行以响应一整秒前发生的点击.我想确保所有输入事件都会触发,但我的函数会在它们之后触发.

更新3:我在使用这个测试平台后发布了我的答案,这很有启发性:http://jsfiddle.net/TrevorBurnham/uJxQB/

将鼠标移到框上(触发1秒的阻塞循环),然后单击多次.在循环之后,您执行的所有点击都会播出:顶部框的click处理程序将其翻转到另一个框下,然后接收下一个框,click依此类推.mouseenter在单击事件之后,回调中触发的超时不会始终发生,并且即使在相同的硬件和操作系统上,单击事件发生所需的时间也会因浏览器而异.(这个实验出现了另一个奇怪的事情:mouseenter即使我将鼠标稳稳地移动到盒子中,我有时会得到多个jQuery 事件.不确定那里发生了什么.)

Tre*_*ham 2

在这一点上,我准备说,遗憾的是,这个问题没有一个解决方案可以在所有浏览器、每种情况下、每次都有效。简而言之:如果运行 JavaScript 函数,则无法可靠地区分用户此期间触发的输入事件和用户之后触发的输入事件。这对于 JS 开发人员,尤其是那些使用交互式画布的开发人员来说,具有有趣的意义。

\n\n

我关于 JS 输入事件如何工作的思维模型是错误的。我以为它去了

\n\n
    \n
  1. 用户在代码运行时单击 DOM 元素
  2. \n
  3. 如果该元素有click事件处理程序,则回调将排队
  4. \n
  5. 当所有阻塞代码执行完毕后,回调将运行
  6. \n
\n\n

然而,我的实验以及弗拉基米尔·帕兰特(Wladimir Palant)贡献的实验(感谢弗拉基米尔)表明正确的模型是

\n\n
    \n
  1. 用户在代码运行时单击 DOM 元素
  2. \n
  3. 浏览器捕获点击的坐标等
  4. \n
  5. 在执行所有阻塞代码一段时间后,浏览器检查哪个 DOM 元素位于这些坐标,然后运行回调(如果有)
  6. \n
\n\n

我说“一段时间后”是因为不同的浏览器似乎对 Chrome for Mac 中的 this\xe2\x80\x94 有非常不同的行为,我可以setTimeout func2, 0在阻止代码的末尾设置 a 并期望func2在单击回调(运行阻塞代码完成后仅1-3ms);但在 Firefox 中,超时总是首先解决,并且点击回调通常在阻止代码执行完毕后约 40 毫秒发生。这种行为显然超出了任何 JS 或 DOM 规范的范围。正如 John Resig 在他的经典《JavaScript 定时器工作原理》中所说:

\n\n
\n

当异步事件发生时(例如鼠标单击、计时器触发或 XMLHttpRequest 完成),它会排队等待稍后执行(这种排队实际发生的方式肯定因浏览器而异,因此请将此视为一种简化)。

\n
\n\n

(强调我的。)

\n\n

那么从实际角度来看这意味着什么呢?这不是问题,因为阻塞代码的执行时间接近 0。这意味着这个问题是遵循旧建议的另一个原因:将 JS 操作分解为小块以避免阻塞线程。

\n\n

正如无用代码所建议的那样,当您可以使用它们\xe2\x80\x94 时,网络工作者会更好,但请注意,您放弃了与 Internet Explorer 和所有主要移动浏览器的兼容性

\n\n

最后,我希望浏览器制造商将来能够在标准化输入事件方面取得进展。这是该地区的众多怪癖之一。我希望 Chrome 能够引领未来:出色的线程隔离、低事件延迟和相对一致的排队行为。Web 开发人员可以梦想,不是吗?

\n