setTimeout还是setInterval?

据我所知,这两个JavaScript的行为方式相同:

备选办法A:

function myTimeoutFunction(){doStuff();setTimeout(myTimeoutFunction, 1000);}
myTimeoutFunction();

备选案文B:

function myTimeoutFunction(){doStuff();}
myTimeoutFunction();setInterval(myTimeoutFunction, 1000);

使用设置超时setInterval有什么区别吗?

404360 次浏览

它们基本上尝试做同样的事情,但setInterval方法比setTimeout方法更准确,因为setTimeout等待1000ms,运行函数,然后设置另一个超时。所以等待时间实际上略多于1000ms(如果你的函数需要很长时间来执行,则会更多)。

尽管有人可能会认为setInterval会每1000ms执行完全一次,但重要的是要注意setInterval也会延迟,因为JavaScript不是多线程语言,这意味着-如果脚本的其他部分正在运行-间隔将不得不等待它完成。

这个小提琴中,您可以清楚地看到超时将落后,而间隔几乎一直保持在几乎1个调用/秒(脚本正在尝试这样做)。如果您将顶部的速度变量更改为20这样的小变量(这意味着它将尝试每秒运行50次),间隔将永远不会达到平均每秒50次迭代。

延迟几乎总是可以忽略不计的,但是如果你正在编程一些非常精确的东西,你应该选择自调整定时器(本质上是一个基于超时的计时器,它会不断调整自己以适应它创建的延迟)

我使用setTimeout。

显然区别在于setTimeout调用一次方法,setInterval重复调用它。

这是一篇解释差异的好文章:教程:带有setTimeout和setInterval的JavaScript计时器

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);

如果您想取消超时,我发现setTimeout方法更容易使用:

function myTimeoutFunction() {doStuff();if (stillrunning) {setTimeout(myTimeoutFunction, 1000);}}
myTimeoutFunction();

此外,如果函数出现错误,它将在第一次错误时停止重复,而不是每秒钟重复一次错误。

setInterval和setTimeout都返回一个计时器ID,您可以使用它来取消执行,即在触发超时之前。要取消,您可以像这样调用clearInterval或clearTimeout:

var timeoutId = setTimeout(someFunction, 1000);clearTimeout(timeoutId);var intervalId = setInterval(someFunction, 1000),clearInterval(intervalId);

此外,当您离开页面或关闭浏览器窗口时,超时会自动取消。

有什么区别吗?

是的。Timeout在调用setTimeout()后执行一定的时间;Interval在触发前一个间隔后执行一定的时间。

如果你的doStuff()函数需要一段时间才能执行,你会注意到差异。例如,如果我们用.表示对setTimeout/setInterval的调用,用*表示超时/间隔的触发,用[-----]表示JavaScript代码执行,时间线如下所示:

Timeout:
.    *  .    *  .    *  .    *  .[--]    [--]    [--]    [--]
Interval:
.    *    *    *    *    *    *[--] [--] [--] [--] [--] [--]

下一个复杂的情况是,如果一个间隔触发,而JavaScript已经忙于做某事(例如处理上一个间隔)。在这种情况下,间隔会被记住,并且在前一个处理程序完成并将控制权返回浏览器时发生。例如,对于一个doStuff()进程,它有时短 ([-]) 有时长 ([-----]):

.    *    *    •    *    •    *    *[-]  [-----][-][-----][-][-]  [-]

•表示无法立即执行其代码的间隔触发,而是被设置为挂起。

因此,间隔尝试“赶上”以按时返回。但是,它们不会一个接一个地排队:每个间隔只能有一个执行挂起。(如果它们都排队了,浏览器将留下不断扩大的未执行列表!)

.    *    •    •    x    •    •    x[------][------][------][------]

x表示无法执行或挂起的间隔触发,因此被丢弃。

如果您的doStuff()函数习惯性地需要比为其设置的时间间隔更长的时间来执行,浏览器将占用100%的CPU来尝试为其提供服务,并且可能会变得响应较差。

你用什么,为什么?

Chained-Timeout为浏览器提供了一个有保证的空闲时间;Interval试图确保它正在运行的功能尽可能接近其预定时间执行,而牺牲浏览器UI可用性。

我会考虑一次性动画的间隔,我希望尽可能平滑,而链式超时对于正在进行的动画更礼貌,这些动画将在页面加载时一直发生。对于要求不高的用途(例如每30秒触发一次琐碎的更新程序或其他东西),您可以安全地使用两者。

在浏览器兼容性方面,setTimeout早于setInterval,但您今天遇到的所有浏览器都支持两者。多年来最后一次落后的是WinMo<6.5的IE Mobile,但希望这也已经过去了。

嗯,正如我刚刚了解到的,setTimeout在一种情况下更好。我总是使用setInterval,我让它在后台运行了半个多小时。当我切换回那个选项卡时,幻灯片(使用代码的幻灯片)变化得非常快,而不是它应该每5秒变化一次。事实上,当我更多地测试它时,它确实会再次发生,是否是浏览器的错并不重要,因为使用setTimeout这种情况是完全不可能的。

我对setInterval(func, milisec)进行了简单的测试,因为我很好奇当函数时间消耗大于间隔持续时间时会发生什么。

setInterval一般在前一次迭代的开始之后安排下一次迭代,除非该功能仍在进行中。如果是这样,setInterval将等待,直到函数结束。一旦发生,函数立即再次被触发-没有根据计划等待下一次迭代(因为它将在没有时间超过函数的条件下)。也不存在并行迭代运行的情况。

我已经在Chromev23上测试过了。我希望它是所有现代浏览器的确定性实现。

window.setInterval(function(start) {console.log('fired: ' + (new Date().getTime() - start));wait();}, 1000, new Date().getTime());

控制台输出:

fired: 1000    + ~2500 ajax call -.fired: 3522    <------------------'fired: 6032fired: 8540fired: 11048

wait函数只是一个线程阻塞助手-同步ajax调用,它在服务器端只需要2500毫秒的处理:

function wait() {$.ajax({url: "...",async: false});}

我认为SetIntervalSetTimeout是不同的。SetInterval根据设置的时间执行块,而SetTimeout执行一次代码块。

在超时倒计时秒后尝试这些代码集:

setInterval(function(e){alert('Ugbana Kelvin');}, 2000);

然后尝试

setTimeout(function(e){alert('Ugbana Kelvin');}, 2000);

你可以看到自己的差异。

setInterval()是否必选

setInterval()是一个基于时间间隔的代码执行方法,它具有在达到间隔时重复运行指定脚本的本机能力。脚本作者应该将没有嵌套到其回调函数中以使其循环,因为它默认循环。除非您调用clearInterval(),否则它将继续在间隔内触发。

如果您想循环动画或时钟滴答的代码,请使用setInterval()

function doStuff() {alert("run your code here when time interval is reached");}var myTimer = setInterval(doStuff, 5000);

设置超时

setTimeout()是一种基于时间的代码执行方法,当达到间隔时,它将仅执行一次脚本。它将没有再次重复,除非您通过将setTimeout()对象嵌套在它调用的函数内部来使其循环脚本。如果面向循环,它将继续在间隔内触发,除非您调用clearTimeout()

function doStuff() {alert("run your code here when time interval is reached");}var myTimer = setTimeout(doStuff, 5000);

如果您希望某些事情在指定的时间段后发生一次,请使用setTimeout()。这是因为当达到指定的间隔时,它只执行一次。

控制台的区别很明显:

输入图片描述

不同之处在于他们的目的。

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

当你在setInterval中运行某个函数时,它的工作时间比timeout->浏览器会卡住。

-例如,doStuff()方法名称需要1500秒。要执行,您执行:setInterval(doStuff,1000);
1)浏览器运行doStuff()方法名称,需要1.5秒。被执行;
2)约1秒后,它再次尝试运行doStuff()方法名称。但是之前的doStuff()方法名称仍然执行->所以浏览器将此运行添加到队列中(在第一次完成后运行)。
3,4,…)相同的添加到下一次迭代的执行队列中,但上一次的doStuff()方法名称仍在进行中…
结果-浏览器卡住了。

要防止这种行为,最好的方法是运行模拟setInterval的setTimeout中的setTimeout
要纠正setTimeout调用之间的超时,您可以使用JavaScript的setInterval的自校正替代方案技术。

只是补充已经说过的内容,但代码的setTimeout版本也将到达Maximum call stack size,这将阻止它运行。由于递归函数没有停止的基本情况,因此您不能让它运行永远。

当您运行以下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);});

稍微换个角度看:setInterval确保代码在每个给定的间隔(即1000ms,或您指定的时间)运行,而setTimeout设置它“等待”运行代码的时间。由于运行代码需要额外的毫秒,它加起来是1000ms,因此setTimeout以不精确的时间(超过1000ms)再次运行。

例如,计时器/倒计时不是用setTimeout完成的,而是用setInterval完成的,以确保它不会延迟并且代码以精确的给定间隔运行。

你的代码会有不同的执行Inevals,在某些项目中,例如在线游戏,这是不可接受的。首先,你应该做什么,为了让你的代码使用相同的Inevals,你应该将“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毫秒,结果将是稳定的。

如果您将setInterval中的间隔设置得太短,它可能会在之前对该函数的调用完成之前触发。我在最近的浏览器(Firefox 78)中遇到了这个问题。它导致垃圾回收机制无法足够快地释放内存并建立了巨大的内存泄漏。使用setTimeout(function, 500);给了垃圾回收机制足够的时间来清理并保持内存稳定。

Serg Hospodarets在他的回答中提到了这个问题,我完全同意他的观点,但他没有包括内存泄漏/垃圾回收机制的问题。我也经历了一些冻结,但是内存使用量很快就达到了4 GB,用于一些微小的任务,这对我来说是真正的麻烦。因此,我认为在我的情况下,这个答案仍然对其他人有益。我本想把它放在评论中,但缺乏这样做的声誉。希望你不介意。

备选办法a备选案文B看起来工作方式相同的原因主要是因为setIntervalsetTimeout函数的位置。

function myTimeoutFunction(){doStuff();setTimeout(myTimeoutFunction, 1000);}
myTimeoutFunction();

这是一个递归函数,如果doStuff非常复杂,setTimeout必须跟踪setTimout加上当前doStuff的所有调用,这使它成为s l o w e r

function myTimeoutFunction(){doStuff();}
myTimeoutFunction();setInterval(myTimeoutFunction, 1000);

另一方面,setInterval只需要跟踪最后一个setInterval和当前的doStuff,使其保持恒定的速度。

那么你应该用哪一个呢?

从上面,你应该能够得出结论,更好的是setInterval

要考虑的重点是性能。使用setTimeout定期运行函数的唯一方法是用目标函数递归调用它,当你检查它时,它似乎是异步工作的当你看到调用堆栈时,你会发现它一直在增长。事实上,这是明智的。由于Javascript不支持多线程,因此不可能在完成子函数之前完成对父函数的调用,因此,只要有递归调用,堆栈就会继续增长。而对于setInterval,我们不需要递归调用目标函数,因为它有一个逻辑,它作为循环定期运行。所以,这保持了调用堆栈的清洁。您可以在浏览器中使用开发人员工具查看调用堆栈,您会注意到差异。

当长时间使用小间隔时,区别会很明显。