javascript - 为什么setTimeout((fn, 0) 有时非常有用的?

  显示原文与译文双语对照的内容

我最近遇到了一个相当令人讨厌的Bug,其中代码通过JavaScript动态加载一个 <select> 。 这个动态加载的<select> 有一个pre-selected值。 在IE6中,我们已经有了修复选中的<option>的代码,因为有时值的<select>selectedIndex 与选择的属性的<option>index 不一致,如下所示:


field.selectedIndex = element.index;

但是,这里代码不起作用。 即使正确设置了字段的selectedIndex,也会选择错误的索引。 但是,如果我在正确的时间粘贴一个 alert() 语句,正确的选项将被选中。 考虑到这可能是某种定时问题,我尝试了一些随机的东西,我在代码中看到过:


var wrapFn = (function() {
 var myField = field;
 var myElement = element;

 return function() {
 myField.selectedIndex = myElement.index;
 }
})();
setTimeout(wrapFn, 0);

而且这一切 !

我有一个解决问题的方法,但我不太清楚为什么我的问题会解决我的问题。 有人有正式的解释? 什么浏览器问题我避免通过调用函数使用 setTimeout()"稍后"?

时间:

这工作是因为你正在合作 multi-tasking 。

浏览器必须一次性完成许多事情,其中一个是执行 JavaScript 。 但是JavaScript经常使用的一个特性是要求浏览器构建一个显示元素。 这通常是通过同步的( 尤其是JavaScript不是并行执行的) 来实现的,但是不能保证这种情况,并且JavaScript没有一个定义良好的机制用于等待。

解决方案是"暂停"执行,让渲染线程赶上。 这是影响 setTimeout() 超时的0 。 它就像一个线程/进程收益率在C 中。 虽然它似乎说"立即运行这里命令"实际上使浏览器有机会完成做一些non-JavaScript东西一直在等待完成之前参加到这个新块javascript。

( 实际上,setTimeout() re-queues在执行队列末尾的新 JavaScript ) 。 有关更多解释的链接,请参阅注释。)

IE6恰好更容易出现这种错误,但我看到它出现在旧版本的Mozilla和 Firefox 。

setTimeout() 在加载DOM元素之前给你提供一些时间,即使设置为 0.

检查这里输出:setTimeout

大多数浏览器都有一个名为主用户界面线程的进程,负责执行JavaScript和用户界面更新 比如 绘图,重画或者重排。

每个JavaScript执行和用户界面更新任务都被添加到浏览器事件队列系统,然后这些任务被调度到浏览器主用户界面线程来执行。

当用户界面线程忙时生成用户界面更新,任务被推入用户界面队列系统。

setTimeout(fn, 0); 在 0毫秒后将任务添加到用户界面队列系统。

这样做的一个原因是将代码执行推迟到一个单独的后续事件循环。 当对某种( 鼠标单击,例如)的浏览器事件做出响应时,有时需要在处理当前事件后只执行操作。 setTimeout() 工具是最简单的方法。

由于它正在传递 0的持续时间,我想它是为了从执行流中删除传递给 setTimeout的代码。 所以如果它是需要一段时间的函数,它将不会阻止后续代码执行。

这是旧答案的旧问题。 我想添加一个新的看这个问题,回答为什么发生这种情况,而不是为什么这是有用的。

你有两个函数:


var f1 = function () { 
 setTimeout(function(){
 console.log("f1","First function call...");
 }, 0);
};

var f2 = function () {
 console.log("f2","Second call...");
};

然后叫他们按照以下顺序 f1(); f2(); 先看到第二个执行。

这里是原因:不可能有 setTimeout 0毫秒的延时。 最小值由浏览器确定,它不是 0毫秒。 历史浏览器设置最低 10毫秒,但html5规格和现代浏览器设置在 4毫秒。

如果嵌套级别大于 5,并且超时小于 4,则将超时增加到 4.

也来自 mozilla:

要在现代浏览器中实现 0毫秒超时,你可以使用 window.postMessage(),如下面的所示。

P.S 。读取下列文章 。后获取信息。

这里有冲突的upvoted答案,没有证明,没有办法知道应该相信谁。 @DVK 是正确的,@SalvadorDali 是错误的。 后者声明:

"和原因:不可能有 0毫秒延迟的setTimeout 。 最小值由浏览器决定,而不是 0毫秒。 历史浏览器将这个最小设置为 10毫秒,但HTML5规范和现代浏览器在 4毫秒内设置。"

4毫秒最短超时与正在发生的情况无关。 真正发生的是setTimeout将回调函数推送到执行队列的末尾。 如果在 setTimeout(callback, 0 ) 之后,你有了需要几秒钟才能运行的代码,那么这个回调将不会被执行几秒钟,直到阻塞代码完成。 请尝试以下代码:


function testSettimeout0 () {
 var startTime = new Date().getTime()
 console.log('setting timeout 0 callback at ' +sinceStart())
 setTimeout(function(){
 console.log('in timeout callback at ' +sinceStart())
 }, 0)
 console.log('starting blocking loop at ' +sinceStart())
 while (sinceStart() <3000) {
 continue
 }
 console.log('blocking loop ended at ' +sinceStart())
 return//functions below
 function sinceStart () {
 return new Date().getTime() - startTime
 }//sinceStart
}//testSettimeout0

输出是:


setting timeout 0 callback at 0
starting blocking loop at 5
blocking loop ended at 3000
in timeout callback at 3033

另一件事是将函数调用推入堆栈的底部,如果递归调用函数,则防止堆栈溢出。 这有一个 while 循环的效果,但允许JavaScript引擎触发其他异步计时器。

...