javascript – 为什么在事件回调之前执行间隔函数?

我意识到关于滚动事件的javascript有些奇怪.

直到现在,我始终相信只要滚动位置发生变化,就会直接触发它.
而且因为javascript是阻塞的,回调总是第一个被执行的东西和第一个“看到”这个新值的函数 – 或者我认为.

这里我有简单的设置,其中全局值在滚动时更新到当前滚动位置.
然后有一个比较两者的间隔.
现在,如果您非常快地滚动,有时会发生缓存的值与实际返回的滚动位置不同.

// cache scroll pos
var scrollPos = 0;
// update on scroll
window.onscroll = function () {
    scrollPos = scrollTop();
};
// compare on interval
setInterval(function () {
    if (scrollPos != scrollTop()) {
        console.log("Out of sync!  cached:", scrollPos, "| actual:", scrollTop());
    }
}, 100);

小提琴:http://jsfiddle.net/71vdx4rv/2/
您可以尝试快速滚动或使用按钮.
[在Chrome,Safari,FF,Opera上测试]

为什么在回调之前执行了区间函数并且知道“新”滚动位置?
有人可以向我解释一下吗?

解决方法:

Until now I was always convinced that it would be fired directly, whenever the scroll position changes.

不幸的是(显然)事实并非如此.而DOM 3 does state

A user agent MUST dispatch [the scroll] event when a document view or an element has been scrolled. This event type is dispatched after the scroll has occurred.

这并不意味着“立即”. CSSOM draft clarifies

[To perform a scroll, repeatedly run these steps]:

  1. Perform a [smooth or instant] scroll of box to position.
  2. Queue a task to run task, unless a task to run task is in the queue.

[where task is does fire a scroll event at the respective target]

“排队任务”明确表示事件是异步调度的,因此在滚动事件触发任务之前执行等待任务(如计时器)时,滚动位置可能已经更改.

此外,当执行平滑滚动时,允许浏览器在排队任务以触发滚动事件之前在任意时间跨度上多次改变滚动位置.

上一篇:javascript – 为什么Firefox setInterval回调参数与其他浏览器不同?


下一篇:javascript – 如何跳过setInterval函数的第一个延迟?