gpt4 book ai didi

javascript - 什么会导致 setTimeout(callback, 0) 的回调延迟

转载 作者:行者123 更新时间:2023-12-03 16:54:21 39 4
gpt4 key购买 nike

有谁知道什么会导致调用传递给 setTimeout(func, 0) 的函数出现明显延迟?我发现在执行 setTimeout(func, 0) 和实际调用 func 之间有超过 1.2 秒的延迟。

我只在 Chrome v52 中看到这种行为(v51 没有)。所以,这可能是 Chrome 的问题,但在我将其报告为错误之前,我想调查一下。

不幸的是,我没有可以复制它的小箱子。它发生在 AngularJS 应用程序中以响应滚动事件。我们处理由鼠标滚轮触发的滚动事件,作为其中的一部分,调用 setTimeout(func, 0) 来做一些后续工作。

使用 Chrome devtools,我可以看到在这 1.2 秒的间隙中运行的 javascript 非常少。我没有阻塞线程。事实上,我使用 console.profile 和 console.profileEnd 来分析特定的 1.2 秒 grap。它表明它有 99% 的时间处于空闲状态。

查看 devtools 中的时间线,我在那个时间段内唯一看到触发的是对“鼠标滚轮”事件的一些处理。其中有很多(大约在间隔的前 1 秒内每 5 毫秒出现一次)。这本身有点奇怪,因为鼠标滚轮在那段时间没有滚动。我轻按一下 Mac Magic Mouse 的虚拟滚轮,我们就进入了可滚动区域的顶部。进入顶部后的事件,我们不断收到鼠标滚轮事件。

有人有什么想法吗?快速执行的少量鼠标滚轮事件是否会延迟回调的调用?有什么方法可以调查超时回调在预定时间后 1.2 秒被调用的原因吗?

编辑:添加了描述 setTimeout 显示回调的时间线图像。

Timeline of slow setTimeout

最佳答案

setTimeout(..., 0) 将事件放入事件队列。但它不会优先于已放入队列的其他事件。当然,当您处理滚动事件时,您会遇到这样的情况:许多滚动事件被放入队列,而滚动事件处理程序代码仍在运行以处理先前的滚动事件。

因此,实际上您在处理滚动事件时很容易陷入延迟。当您的调用堆栈为空时,即当前正在运行的事件处理程序完成时,将从事件队列中获取下一个事件。即使在某个队列中存在与超时相关的事件,它也不会获得更高的优先级:所有事件都必须等待轮到它们。

例如,如果队列中已经有 10 个滚动事件,它们都必须被处理,当鼠标仍在滚动时,问题只会变得更糟,因为它可以添加更多的事件到队列,并以您的代码可以处理它们的更高速率。

解决这个问题的一种方法是让滚动事件处理程序尽可能轻,并异步处理繁重的事情。如果处理程序检测到在之前的运行中某些东西已经计划以这种方式运行,它可以简单地取消该计划的任务,并将其替换为新版本。

示例代码:

var task = -1;
window.addEventListener('scroll', function(e) {
clearTimeout(task);
task = setTimeout(function () {
// all the (heavy) processing related to scrolling comes here.
}, 0);
};

不要将此 setTimeout 与问题中的混淆。但请注意,当您启动另一个 setTimeout 时,它将按顺序处理。上面的代码只是将实际处理移动到队列的末尾。但是,如果有更多的滚动事件,它们将分别从队列中删除那个超时事件,并在队列的末尾放置一个新事件。

这会产生这样的效果,即您实际上会较少执行滚动处理程序代码的重部分,这可能会对某些动画的流动性产生一些负面影响。但它会防止滞后,作为奖励,您的其他 setTimeout 事件将更快轮到它。

关于javascript - 什么会导致 setTimeout(callback, 0) 的回调延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38960779/

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com