立即调用setTimeout

时间:2012-01-31 16:56:59

标签: javascript settimeout

经常在JavaScript库中看到这样的代码:

setTimeout(function() {
    ...
}, 0);

我想知道为什么要使用这样的包装代码。

5 个答案:

答案 0 :(得分:55)

非常简化:

浏览器是单线程的,这个单线程(UI线程)在呈现引擎和js引擎之间共享。

如果你想要做的事情需要很多时间(我们在这里讨论周期但仍然存在),它可能会停止(paus)渲染(流动和绘画)。

在浏览器中还存在“存储桶”,其中所有事件首先被放入,等待UI线程完成任何操作。一旦线程完成,它就会在存储桶中查找并首先选择任务。

使用setTimeout在延迟之后在存储桶中创建一个新任务,让线程在可用于更多工作时立即处理它。

一个故事:

  

延迟0 ms后,创建该功能的新任务   并把它放在桶里。在那个确切的时刻,UI线程正忙   做其他事情,还有另外一个任务   已经。在6ms之后,该线程可用并获得面前的任务   你的,好的,你是下一个。但是什么?那是一件大事!它有   就像上帝(30ms)!!

     

最后,现在线程已经完成,然后来获取你的   任务。

大多数浏览器的最小延迟大于0,因此将0作为延迟意味着:尽快将此任务放在购物篮中。但是告诉UA尽快将其放入存储桶并不能保证它会在那一刻执行。这个桶就像邮局一样,可能是因为有很长的其他任务。邮局也是单线程的,只有一个人帮助完成所有任务......抱歉客户完成任务。你的任务必须和其他人一样。

如果浏览器没有实现自己的自动收报机,它会使用操作系统的滴答周期。较旧的浏览器具有10-15ms之间的最小延迟。 HTML5 specifies如果延迟小于4ms,则UA应将其增加到4ms。据说这是consistent across browsers released in 2010 and onward

有关详细信息,请参阅John Resig的How JavaScript Timers Work

编辑:另请参阅JSConf EU 2014的Philip Roberts撰写的What the heck is the event loop anyway?。这是所有触及前端代码的人的强制性观看。

答案 1 :(得分:9)

为什么要这样做有几个原因

  • 您不希望立即执行某项操作,但希望在不久的将来某个时段运行。
  • 您希望允许setTimeoutsetInterval中其他先前注册的处理程序运行

答案 2 :(得分:6)

除了以前的答案,我还想添加另一个我能想到的有用场景:从try-catch块中“逃脱”。 try-catch块内的setTimeout-delay将在块外执行,任何异常都将在全局范围内传播。

也许最好的示例场景:在今天的JavaScript中,对于异步回调所谓的Deferreds / Promises的更常见的使用,你(通常)实际上在try-catch中运行。 Deferreds / Promises将回调包装在try-catch中,以便能够在异步链中检测并传播异常作为错误。这对于需要在链中的函数来说都是有用的,但是迟早你已经“完成”(即获取所有的ajax)并且想要运行普通的非异步代码,你不希望异常“隐藏“了。 AFAIK Dojo,Kris Kowal的Q,MochiKit和Google Closure lib使用try-catch包装(虽然不是jQuery)。

(在几个奇怪的场合,我也使用了这种技术来重启单例式代码而不会导致递归。即在同一个循环中进行拆机重启。)

答案 3 :(得分:3)

当您想要执行其余代码而不等待前一个代码完成时,您需要将其添加到传递给setTimeout函数的匿名方法中。否则,您的代码将等到上一个完成

示例:

function callMe()
{
   for(var i = 0; i < 100000; i++)
     {
       document.title = i;
     }
} 

var x = 10;
setTimeout(callMe, 0);

var el = document.getElementById('test-id');
el.innerHTML = 'Im done before callMe method';

这就是我使用它的原因。

答案 4 :(得分:1)

允许执行任何先前设置的超时。