设置超时或设置间隔?
Posted
技术标签:
【中文标题】设置超时或设置间隔?【英文标题】:setTimeout or setInterval? 【发布时间】:2010-10-18 07:30:05 【问题描述】:据我所知,这两个 javascript 的行为方式相同:
选项 A:
function myTimeoutFunction()
doStuff();
setTimeout(myTimeoutFunction, 1000);
myTimeoutFunction();
选项 B:
function myTimeoutFunction()
doStuff();
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);
使用setTimeout和setInterval有什么区别吗?
【问题讨论】:
如果您想了解有关 JS 中的计时器如何工作的详细信息,John Resig 就该主题写了一篇很好的 article 还有一个明显的区别是 setTimeout 需要额外的代码行来保持它的传播,这有一个维护问题的缺点,但让你可以轻松更改周期的好处 试试这个jsfiddle.net/pramendra/Y4vEV 感谢@JapanPro,但我从来没有真正遇到过超时工作的问题。这篇文章是关于区别是什么以及应该使用哪个。 更新链接。编辑队列已满setTimeout VS setInterval 【参考方案1】:他们基本上尝试做同样的事情,但setInterval
方法将比setTimeout
方法更准确,因为setTimeout
等待 1000 毫秒,运行函数然后设置另一个超时。所以等待时间实际上比 1000 毫秒多一点(如果你的函数需要很长时间才能执行,或者更多)。
虽然有人可能认为setInterval
会精确 每 1000 毫秒执行一次,但需要注意的是,setInterval
也会延迟,因为 JavaScript 不是多线程语言,这意味着那 - 如果脚本的其他部分正在运行 - 间隔将不得不等待它完成。
在this Fiddle 中,您可以清楚地看到超时将落后,而时间间隔几乎一直在几乎 1 次调用/秒(脚本试图这样做)。如果将顶部的速度变量更改为 20 之类的小值(这意味着它将尝试每秒运行 50 次),则间隔永远不会达到平均每秒 50 次迭代。
延迟几乎总是可以忽略不计,但如果您正在编写一些非常精确的程序,您应该选择一个自调整计时器(它本质上是一个基于超时的计时器,它会不断地自我调整它创建的延迟)
【讨论】:
安迪也有类似的建议。假设,这是否意味着如果该方法的执行时间超过 1000 毫秒,您可以同时运行多个? 理论上,是的。实际上,没有,因为 Javascript 不支持多线程。如果您的代码花费的时间超过 1000 毫秒,它将冻结浏览器。 从技术上讲,代码不会每 1000 毫秒精确执行一次,因为它取决于计时器的分辨率以及其他代码是否已经在执行。你的观点仍然成立。 setInterval 在两个方面有所不同,1。 setInterval 不是递归的,setInterval 将在指定时间后首次调用您的函数,而 setTimeout 第一次调用时不会有任何延迟,之后它将在指定时间后再次调用。第一次执行后,它们的工作方式几乎相同。 不同的是setTimeout不会重复自己。它允许您在设定的时间后运行脚本,但只能运行一次。另一方面,setInterval 将重复该脚本,直到它被 clearTimeout() 停止。【参考方案2】:我使用 setTimeout。
显然不同的是setTimeout调用方法一次,setInterval重复调用。
这里有一篇很好的文章解释了区别:Tutorial: JavaScript timers with setTimeout and setInterval
【讨论】:
是的,我得到了那个区别,但是我提供的两段代码应该做同样的事情...... 嗯,是的...我本以为...但根据 dcaunt 和他的投票结果,情况并非如此。【参考方案3】:setInterval 可以更轻松地取消将来执行代码。如果您使用 setTimeout,则必须跟踪计时器 ID,以防您以后想取消它。
var timerId = null;
function myTimeoutFunction()
doStuff();
timerId = setTimeout(myTimeoutFunction, 1000);
myTimeoutFunction();
// later on...
clearTimeout(timerId);
对
function myTimeoutFunction()
doStuff();
myTimeoutFunction();
var timerId = setInterval(myTimeoutFunction, 1000);
// later on...
clearInterval(timerId);
【讨论】:
在 setInterval 的情况下,我看不出您是如何不跟踪计时器 ID 的。它只是从函数中拉出来的。setTimeout
的另一个选项不是保存 id,而是添加一个 if
语句以仅在某些条件为真时设置下一个超时。
Kekoa,说得好。但是您只需保存一次间隔 id。超时 id 可能会随着每次调用而改变,因此您必须跟踪这些更改。想要清除超时的代码必须以某种方式访问该变量的当前值。此外,在运行doStuff
时无法清除超时,因为 id 无效。但是,保存超时 ID 并不是真正必要的。相反,您可以停止拨打setTimeout
。
根据我的经验,大多数情况下,即使使用 setTimeout,您也希望能够取消。 99% 的时间您希望在 在 下一次调用发生之前取消,而不是 在 下一次调用完成之后。【参考方案4】:
如果您想取消超时,我发现setTimeout
方法更易于使用:
function myTimeoutFunction()
doStuff();
if (stillrunning)
setTimeout(myTimeoutFunction, 1000);
myTimeoutFunction();
此外,如果函数中出现问题,它只会在第一次错误时停止重复,而不是每秒重复一次错误。
【讨论】:
【参考方案5】:setInterval 和 setTimeout 都返回一个计时器 id,您可以使用它来取消执行,即在触发超时之前。要取消,您可以像这样调用 clearInterval 或 clearTimeout:
var timeoutId = setTimeout(someFunction, 1000);
clearTimeout(timeoutId);
var intervalId = setInterval(someFunction, 1000),
clearInterval(intervalId);
此外,当您离开页面或关闭浏览器窗口时,超时会自动取消。
【讨论】:
【参考方案6】:有什么不同吗?
是的。一个 Timeout 在 setTimeout() 被调用后执行一定的时间;间隔在上一个间隔触发后执行一定的时间。
如果您的 doStuff() 函数需要一段时间才能执行,您会注意到差异。例如,如果我们用.
表示对setTimeout/setInterval 的调用,用*
表示超时/间隔的触发,用[-----]
表示JavaScript 代码执行,则时间线如下所示:
Timeout:
. * . * . * . * .
[--] [--] [--] [--]
Interval:
. * * * * * *
[--] [--] [--] [--] [--] [--]
下一个复杂情况是,如果在 JavaScript 已经忙于做某事(例如处理前一个间隔)时触发了间隔。在这种情况下,间隔会被记住,并在前一个处理程序完成并将控制权返回给浏览器时立即发生。例如,对于一个有时很短 ([-]) 有时很长 ([-----]) 的 doStuff() 过程:
. * * • * • * *
[-] [-----][-][-----][-][-] [-]
• 表示无法立即执行其代码的间隔触发,而是处于挂起状态。
因此,间隔会尝试“赶上”以恢复进度。但是,它们不会将一个放在彼此之上:每个间隔只能有一个待执行的执行。 (如果他们都排队,浏览器会留下一个不断扩大的未完成执行列表!)
. * • • x • • x
[------][------][------][------]
x 表示无法执行或处于挂起状态的间隔触发,因此被丢弃。
如果您的 doStuff() 函数的执行时间习惯性地比为其设置的间隔时间长,则浏览器将占用 100% 的 CPU 来尝试为其提供服务,并且响应速度可能会变慢。
您使用哪个以及为什么?
Chained-Timeout 为浏览器提供有保证的空闲时间; Interval 尝试确保它正在运行的函数尽可能接近其预定时间执行,但会牺牲浏览器 UI 的可用性。
我会考虑一次性动画的间隔,我希望尽可能流畅,而对于在页面加载时一直发生的持续动画来说,链接超时更为礼貌。对于要求不高的用途(例如每 30 秒触发一次的微不足道的更新程序等),您可以安全地使用任何一种。
在浏览器兼容性方面,setTimeout 早于 setInterval,但您今天遇到的所有浏览器都支持这两者。多年来最后一个落后者是 WinMo
【讨论】:
但是在 Interval 和 Timeout 之间进行选择的原因是否适用于非浏览器平台,例如 WebOS 或 JIL? 一种对匿名函数进行链式超时的好方法:setTimeout(function() setTimeout(arguments.callee,10) ,10) 在浏览器兼容性方面,虽然所有的浏览器都提供了这两种方法,但是它们的性能是不一样的。 “但是很可能它也不支持你正在使用的任何其他东西”所以非常正确 drum machine by chromium project using setTimeout("schedule()", 0);因此,当浏览器是 chrome 的背景选项卡或资源不足时,浏览器不会有不必要的堆栈。【参考方案7】:好吧,正如我刚刚了解到的,setTimeout 在一种情况下更好。我总是使用 setInterval,我在后台运行了半个多小时。当我切换回该选项卡时,幻灯片(使用代码的)变化非常迅速,而不是它应该每 5 秒发生一次。事实上,当我对其进行更多测试时,它确实会再次发生,并且它是否是浏览器的错误并不重要,因为使用 setTimeout 这种情况是完全不可能的。
【讨论】:
听起来好像您在调用的函数内部调用了 setInterval。这就是您使用 setTimeout 循环的方式,但使用 setInterval 您实际上是在每次调用它时创建一个全新的循环。【参考方案8】:我对@987654322@ 做了简单的测试,因为我很好奇当函数时间消耗大于间隔持续时间时会发生什么。
setInterval
通常会在上一次迭代的开始之后安排下一次迭代,除非函数仍在进行中。如果是这样,setInterval
将等待,直到函数结束。一旦发生,该函数会立即再次触发 - 无需根据计划等待下一次迭代(因为它会在没有超时函数的条件下)。也没有并行迭代运行的情况。
我已经在 Chrome v23 上对此进行了测试。我希望它是所有现代浏览器的确定性实现。
window.setInterval(function(start)
console.log('fired: ' + (new Date().getTime() - start));
wait();
, 1000, new Date().getTime());
控制台输出:
fired: 1000 + ~2500 ajax call -.
fired: 3522 <------------------'
fired: 6032
fired: 8540
fired: 11048
wait
函数只是一个线程阻塞助手 - 同步 ajax 调用,它在服务器端的处理时间正好是 2500 毫秒:
function wait()
$.ajax(
url: "...",
async: false
);
【讨论】:
“没有并行迭代运行的情况” - 是的,这应该是不可能的。客户端 JavaScript 具有单线程执行模型,因此不会同时发生任何事情(事件处理程序等)。这就是为什么在同步 ajax 调用期间没有任何反应(并且浏览器没有响应)。 浏览器是否在“间隔”之间的几毫秒内响应?如果它正在等待,另一个事件会触发吗? (感谢测试 btw +1) 根据 MDN,如果函数的执行时间超过了间隔时间,则认为是“dangerous usage”。首选递归setTimout
调用。【参考方案9】:
我认为SetInterval
和SetTimeout
是不同的。 SetInterval
按照设定的时间执行代码块,SetTimeout
执行代码块一次。
在超时倒计时秒后尝试这组代码:
setInterval(function(e)
alert('Ugbana Kelvin');
, 2000);
然后尝试
setTimeout(function(e)
alert('Ugbana Kelvin');
, 2000);
您可以自己查看差异。
【讨论】:
这没有回答问题。可以递归使用 settimeout() 来给出与 setinterval 相同的结果。问题是关于这两个选项的性能。【参考方案10】:setInterval()
setInterval()
是一种基于时间间隔的代码执行方法,具有在达到间隔时重复运行指定脚本的本机能力。它应该不由脚本作者嵌套到其回调函数中以使其循环,因为它默认循环。除非你打电话给clearInterval()
,否则它会在间隔内持续触发。
如果您想循环播放动画或时钟滴答的代码,请使用setInterval()
。
function doStuff()
alert("run your code here when time interval is reached");
var myTimer = setInterval(doStuff, 5000);
setTimeout()
setTimeout()
是一种基于时间的代码执行方法,当达到间隔时,它只会执行一次脚本。它不会再次重复,除非您通过将setTimeout()
对象嵌套在它调用运行的函数内来使其循环脚本。如果适合循环播放,除非您调用 clearTimeout()
,否则它将继续按间隔发射。
function doStuff()
alert("run your code here when time interval is reached");
var myTimer = setTimeout(doStuff, 5000);
如果您希望某件事在指定时间段后发生一次,请使用setTimeout()
。那是因为它只在达到指定的时间间隔时执行一次。
【讨论】:
很好的解释,但请注意,OP 了解 OP 提供的示例中的基本区别。特别要注意,在 OP 的setTimeout()
示例中,setTimeout()
被递归调用,而 setInterval()
不是。【参考方案11】:
在控制台中区别很明显:
【讨论】:
【参考方案12】:区别在于它们的目的。
setInterval()
-> executes a function, over and over again, at specified time intervals
setTimeout()
-> executes a function, once, after waiting a specified number of milliseconds
就这么简单
更详细的细节在这里http://javascript.info/tutorial/settimeout-setinterval
【讨论】:
简洁的解释,但请注意,OP 了解 OP 提供的示例的基本区别。特别要注意,在 OP 的setTimeout()
示例中,setTimeout()
被递归调用,而 setInterval()
不是。【参考方案13】:
当你在 setInterval 中运行某个函数时,它的工作时间比超时时间长-> 浏览器会卡住。 - 例如,doStuff() 需要 1500 秒。被执行然后你做:setInterval(doStuff, 1000); 1) 浏览器运行 doStuff() 需要 1.5 秒。被执行; 2) 大约 1 秒后,它会尝试再次运行 doStuff()。但是之前的 doStuff() 仍然被执行-> 所以浏览器将此运行添加到队列中(在第一次完成后运行)。 3,4,..) 相同的添加到下一次迭代的执行队列中,但之前的 doStuff() 仍在进行中... 结果 - 浏览器卡住了。 为防止这种行为,最好的方法是在 setTimeout 中运行 setTimeout 以模拟 setInterval。 要纠正 setTimeout 调用之间的超时,您可以使用self-correcting alternative to JavaScript's setInterval 技术。
【讨论】:
我不知道为什么没有人在这个答案中找到任何价值!【参考方案14】:只是添加到已经说过的内容,但代码的 setTimeout 版本也将到达Maximum call stack size
,这将阻止它运行。由于递归函数没有停止的基本情况,所以你不能让它永远运行。
【讨论】:
我认为这不是真的。见这里***.com/questions/8058612/…【参考方案15】:您可以在运行以下 javascript 或检查 JSFiddle 时自行验证 bobince 答案
<div id="timeout"></div>
<div id="interval"></div>
var timeout = 0;
var interval = 0;
function doTimeout()
$('#timeout').html(timeout);
timeout++;
setTimeout(doTimeout, 1);
function doInterval()
$('#interval').html(interval);
interval++;
$(function()
doTimeout();
doInterval();
setInterval(doInterval, 1);
);
【讨论】:
【参考方案16】:换个角度看:setInterval 确保代码以每个给定的时间间隔(即 1000 毫秒,或您指定的时间间隔)运行,而 setTimeout 设置它“等待”运行代码的时间。由于运行代码需要额外的毫秒数,因此它加起来会达到 1000 毫秒,因此 setTimeout 会在不精确的时间(超过 1000 毫秒)再次运行。
例如,定时器/倒计时不是用 setTimeout 完成的,而是用 setInterval 完成的,以确保它不会延迟并且代码以准确的给定间隔运行。
【讨论】:
【参考方案17】:您的代码将有不同的执行间隔,并且在某些项目中,例如在线游戏,这是不可接受的。首先,你应该怎么做,为了让你的代码使用相同的间隔,你应该把“myTimeoutFunction”改成这样:
function myTimeoutFunction()
setTimeout(myTimeoutFunction, 1000);
doStuff();
myTimeoutFunction()
修改后,将等于
function myTimeoutFunction()
doStuff();
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);
但是,你仍然会得到不稳定的结果,因为 JS 是单线程的。目前,如果 JS 线程忙于某事,它将无法执行您的回调函数,执行将推迟 2-3 毫秒。您是否每秒执行 60 次,并且每次您有 1-3 秒的随机延迟,这绝对是不可接受的(一分钟后它将是大约 7200 毫秒的延迟),我可以建议使用这样的东西:
function Timer(clb, timeout)
this.clb = clb;
this.timeout = timeout;
this.stopTimeout = null;
this.precision = -1;
Timer.prototype.start = function()
var me = this;
var now = new Date();
if(me.precision === -1)
me.precision = now.getTime();
me.stopTimeout = setTimeout(function()
me.start()
, me.precision - now.getTime() + me.timeout);
me.precision += me.timeout;
me.clb();
;
Timer.prototype.stop = function()
clearTimeout(this.stopTimeout);
this.precision = -1;
;
function myTimeoutFunction()
doStuff();
var timer = new Timer(myTimeoutFunction, 1000);
timer.start();
此代码将保证稳定的执行周期。连线程都会忙,你的代码会在1005毫秒后执行,下次会超时995毫秒,结果会稳定。
【讨论】:
【参考方案18】:如果您将setInterval
中的时间间隔设置得太短,它可能会在之前的函数调用完成之前触发。我使用最近的浏览器(Firefox 78)遇到了这个问题。这导致垃圾收集无法足够快地释放内存并造成巨大的内存泄漏。
使用setTimeout(function, 500);
为垃圾收集提供了足够的时间来清理并随着时间的推移保持内存稳定。
Serg Hospodarets 在他的回答中提到了这个问题,我完全同意他的说法,但他没有包括内存泄漏/垃圾收集问题。我也经历了一些冻结,但内存使用量很快就达到了 4 GB,用于一些小任务,这对我来说真是太糟糕了。因此,我认为这个答案在我的情况下仍然对其他人有益。我会把它放在评论中,但缺乏这样做的声誉。希望你不要介意。
【讨论】:
【参考方案19】:选项 A 和 选项 B 之所以工作起来看起来一样,主要是因为 setInterval
和 setTimeout
函数的位置。 p>
function myTimeoutFunction()
doStuff();
setTimeout(myTimeoutFunction, 1000);
myTimeoutFunction();
这是一个递归函数,如果doStuff
非常复杂,setTimeout
必须跟踪setTimout
加上当前doStuff
的所有调用,这使得它变得变慢 和 较慢。
function myTimeoutFunction()
doStuff();
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);
另一方面,setInterval
只需跟踪最后一个setInterval
和当前doStuff
,使其保持恒定 速度。
从以上内容,您应该可以得出结论,更好的是setInterval
。
【讨论】:
【参考方案20】:要考虑的重点是性能。
使用setTimeout
定期运行函数的唯一方法是使用目标函数递归调用它,当你检查它时,它似乎是异步工作的,当你看到调用堆栈时你会发现它一直在增长.事实上,这是明智的。由于Javascript不支持多线程,所以在子函数完成之前不可能完成父函数的调用,因此只要有递归调用,栈就会一直增长。
同时,使用setInterval
,我们不需要递归调用目标函数,因为它有一个逻辑可以将其作为循环定期运行。因此,这使调用堆栈保持清洁。
您可以在浏览器中使用开发者工具查看调用堆栈,您会注意到其中的不同。
长时间使用小间隔,差异会很明显。
【讨论】:
以上是关于设置超时或设置间隔?的主要内容,如果未能解决你的问题,请参考以下文章