setTimeout(fn, 0)和setTimeout(fn, 1)有什么区别?

34

jQuery源代码中使用了setTimeout函数,它的第二个参数有时是0,有时是1。我认为它们的意思都是“尽快执行函数”,这是正确的吗?这两者之间有什么区别吗?


25
1毫秒的差距可能听起来微不足道,但在竞技体育和计算机科学等领域中,它可以产生巨大的影响。 - Leon
2
@Leon:我原本以为浏览器无法以那种精度执行,所以将setTimeout时间增加到最少13毫秒。 - Randomblue
从未听说过那个神奇的13,但是这里有一句话:“在大多数浏览器中,setTimeout不允许小于约10毫秒的延迟(它会强制任何更小的延迟变得更长)”。 - Leon
@Leon,你可能会认为1毫秒会有所差别,但这取决于事件循环中是否还有其他项目。 - Adam Fowler
6个回答

30

setTimeout的最小超时时间为4ms。所以这两者没有区别

如果当前正在运行的任务是由setTimeout()方法创建的任务,并且超时时间小于4,则将超时时间增加到4。

规范

注:正如评论中Ahmad所指出的那样,现在规范已经改变,因此答案会是“这取决于情况。”


1
当所有浏览器都能完全遵循规范到毫秒级别时,对于我们的网页开发者来说,那将是一个伟大的日子 :D - Leon
3
据我回忆,你定义的超时时间实际上是最早可能的时间点。如果你的CPU使用率很高,你的函数可能会延迟很多时间才执行。 - Valentin
8
实际上,你的假设是错误的,在规范中他们提到,如果嵌套级别大于5且超时小于4,则将超时时间增加到4。因此,这仅在嵌套级别大于5时才有效。 - amd
1
@Ahmad,没错,现在已经更新了。在我回答这个问题的时候,大约两年前,规范中有我回答中引用的那些词语。 - Some Guy
1
MDN还在他们的文章并发模型和事件循环中解释了使用setTimeout()与零延迟的用法。 - gfullam
显示剩余3条评论

21

我现在认为答案是“视情况而定”。

我们可以在不同的平台和浏览器中运行代码:

function setTimeouts() {
  setTimeout(function() { console.log(2); }, 2);
  setTimeout(function() { console.log(1); }, 1);
  setTimeout(function() { console.log(0); }, 0);
}

for (var i = 0; i < 10; i++) {
  setTimeouts();
}

  1. For Node.js, 0 is converted to 1, so they are exactly the same: https://github.com/nodejs/node/blob/master/lib/timers.js#L319, and result might be:

     1
     0
     1
     0
     1
     0
     1
     0
     1
     0
     1
     0
     1
     0
     1
     0
     1
     0
     1
     0
     2
     2
     2
     2
     2
     2
     2
     2
     2
     2
    
  2. For Chrome, the result is quite similar with Node.js

  3. For Firefox, most of 0 will be printed before 1:

     0
     0
     0
     0
     0
     0
     0
     0
     0
     0
     1
     1
     1
     1
     1
     1
     1
     1
     1
     1
     2
     2
     2
     2
     2
     2
     2
     2
     2
     2
    

是的,Chrome目前有1毫秒的最小超时时间限制,但他们正在努力消除这个限制:https://crbug.com/402694。Node.js也有相同的1毫秒最小值,但Firefox没有(尽管他们在页面加载前安排的回调比其他回调具有较低的优先级,但他们也存在一些奇怪情况:https://bugzil.la/1270059)。 - Kaiido

5

我不确定给出的答案是否正确。在Chrome中运行以下代码,0明显更快地调用绑定函数(只需在01之间切换计时器值即可):

console.log("A");
console.log("B");
var start = new Date().getTime();
setTimeout(function() {
    console.log(new Date().getTime() - start);
}, 0);
console.log("C");
console.log("D");

0似乎类似于Node.js的setImmediate,将一条指令推送到当前调用堆栈的末尾,而1则调用实现视为最小值的内容。


小修正:你在 Node 中提到的函数叫做 process.nextTick,而 setImmediate 是 IE10 中引入的新功能,它们的作用是相同的。 - Paul Gordon
在上述测试中,我发现使用 01 没有一致的优势。有时输出结果使用 0 更大,有时使用 1 更大。 - gfullam

2
程序上和计算上有所不同,但在执行时你看不出区别,因为它只有1毫秒。
我想如果超时设置为1毫秒,它会暂停该脚本并允许其他脚本同时运行。正如你可能知道的,JavaScript是单线程的,这可能就是原因。
感谢molf纠正了我的想法。看起来将其设置为ms只是一个小技巧,让它在事件循环的下一个tick中运行。

3
为什么使用“0毫秒”也不会发生这种情况呢?你有任何可靠的来源吗? - molf
如果将其设置为0毫秒,那么立即运行是有意义的。我没有任何来源,这就是为什么我写“我想象”的原因。 - Jan Dragsbaek
4
将它设置为 0 是一种常见的技巧,可以使代码在事件循环的下一个时刻运行,而不是立即运行。 - molf
@JanDragsbaek setTimeout 是异步的。此外,根据您的浏览器,它具有 4、10 或 13 毫秒的最小超时时间。 - Raynos

1

关于为什么需要setTimeout(fn, 0)setTimeout(fn, 1)的原因,请查看为什么有时候需要使用 setTimeout(fn,0)?

本质上,这意味着与页面渲染等其他浏览器任务相比,该方法执行的紧迫性不高。此外,JavaScript代码将在等待任务完成后运行。

实际上,在使用0或1之间没有区别。 这只是程序员的选择。 理想情况下,码农选择的数字应低于4,这可能是阿曼指出的原因。

顺便说一句,有关JavaScript定时器的基本信息,请参阅http://ejohn.org/blog/how-javascript-timers-work/


-1

这只是jQuery源代码中不良编程实践的一个例子。

就是这样。没有理由偏爱0而不是1,或者反之。

提交一个jQuery错误报告,将其修复/规范化为使用其中之一。


请定义“糟糕的代码”。您认为是否有解决方案,而不使用setTimeout(跨浏览器),或者只是因为他们使用了1/0?前者我很想听听您的建议,后者最多只是代码异味。 - jAndy
4
@jAndy,我的意思是他们在源代码中同时使用0和1。这种情况不应该经过代码审查。jQuery 应该有一个体面的样式指南,并严格执行它。 - Raynos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接