setTimeout或setInterval?


763

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

选项A:

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

myTimeoutFunction();

选项B:

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

使用setTimeoutsetInterval之间有什么区别?


6
如果您想了解JS中的计时器如何工作的一些详细信息,John Resig撰写了一篇有关该主题的好文章
Rafael

9
还有一个明显的区别,就是setTimeout需要额外的代码行来保持其传播,这样做的缺点是维护问题,但好处是可以轻松更改时间段
annakata


4
感谢@JapanPro,但是让超时工作我从来没有真正遇到过问题。这篇文章是关于区别是什么,应该使用什么。
Damovisa 2011年

Answers:


670

他们本质上试图做同样的事情,但是该setInterval方法比该setTimeout方法更准确,因为要setTimeout等待1000ms,然后运行该函数,然后设置另一个超时。因此,等待时间实际上超过了1000毫秒(如果函数执行时间较长,则等待时间会更长)。

尽管有人可能会认为setInterval将执行完全相同每1000毫秒,这是需要注意的重要setInterval也将推迟,因为JavaScript是不是多线程的语言,这意味着-如果有运行脚本的其他部分-区间将有等待完成。

此Fiddle中,您可以清楚地看到超时将落在后面,而间隔几乎总是以近1个呼叫/秒的速度(脚本正在尝试执行)。如果将顶部的速度变量更改为20之类的小值(这意味着它将尝试每秒运行50次),则该间隔将永远不会达到平均每秒50次迭代的速度。

延迟几乎总是可以忽略不计,但是如果您要编写非常精确的内容,则应使用自调整计时器(本质上是基于超时的计时器,该计时器会根据其创建的延迟不断进行自我调整)


4
安迪也有类似的建议。从理论上讲,这是否意味着如果该方法花费1000毫秒以上的时间来执行,那么您可以同时运行多个?
2009年

14
从理论上讲,是的。实际上,因为Javascript不支持多线程,所以没有。如果您的代码花费的时间超过1000毫秒,则会冻结浏览器。
2009年

61
从技术上讲,该代码不会精确地每1000毫秒执行一次,因为它取决于计时器的分辨率以及是否已经在执行其他代码。您的观点仍然成立。
马修·克鲁姆利

10
setInterval在两个方面有所不同,1。setInterval不是递归的,setInterval将在告知时间后第一次调用您的函数,而setTimeout则在没有任何延迟的情况下首次被调用,此后它将在告知时间后再次调用。第一次执行后,它们的工作几乎相同。
哈菲兹

5
区别在于setTimeout不会重复自身。它允许您在设置的时间后运行脚本,但只能运行一次。另一方面,setInterval将重复该脚本,直到使用clearTimeout()将其停止。
Leander

648

有什么区别吗?

是。在调用setTimeout()之后,超时将执行一定的时间;间隔在上一个间隔触发后执行一定的时间。

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

Timeout:

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

Interval:

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

下一个复杂的问题是,如果在JavaScript已经忙于执行某项操作(例如处理上一个间隔)的同时触发了间隔,则该操作会变得很复杂。在这种情况下,该间隔将被记住,并在前一个处理程序完成并将控制权返回给浏览器后立即发生。例如,对于一个有时短([-])有时长([-----])的doStuff()过程:

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

•表示无法立即执行其代码的间隔触发,而是改为等待执行。

因此,间隔尝试“赶上”以恢复原定计划。但是,它们不会彼此排长队:每个间隔只能有一个执行挂起。(如果他们都排队,那么浏览器将得到不断扩大的出色执行列表!)

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

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

如果您的doStuff()函数习惯性地花费比为其设置的时间间隔执行的时间长,则浏览器将消耗100%的CPU尝试为其服务,并且响应速度可能会变慢。

您使用哪个?为什么?

链接超时为浏览器提供了一定的空闲时间。Interval尝试确保正在运行的功能尽可能接近其计划时间执行,但以牺牲浏览器UI可用性为代价。

我会考虑一次平滑的动画间隔,希望尽可能平滑,而链式超时对于在页面加载过程中始终出现的正在进行的动画更礼貌。对于要求不高的用途(例如每30秒触发一次琐碎的更新程序),您可以放心使用。

在浏览器兼容性方面,setTimeout早于setInterval,但是您今天将要使用的所有浏览器都支持这两种。多年以来的最后一次失败是WinMo <6.5中的IE Mobile,但希望现在也已经过去。


但是,即使在非浏览器平台(例如WebOS或JIL)上,在“间隔”和“超时”之间进行选择的原因是否也适用?
Vid L

2
在匿名函数上执行链接超时的一种好方法:setTimeout(function(){setTimeout(arguments.callee,10)},10)
Justin Meyer 2010年

1
在浏览器兼容性方面,尽管所有浏览器都提供这两种方法,但是它们的性能有所不同。
unigg 2010年

“但是很有可能它不支持您正在使用的任何其他东西”,所以非常正确
基本的

1
铬项目使用setTimeout(“ schedule()”,0)制作鼓机 因此,当浏览器是Chrome的背景标签或资源不足时,浏览器不会出现不必要的堆栈。
Elliot Yap 2012年

90

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()。这是因为仅在达到指定间隔时才执行一次。


6
很好的解释,但请注意,OP了解OP提供的示例中的基本区别。特别要注意的是,在OP的setTimeout()例子,setTimeout()被称为递归,而setInterval()不是。
DavidRR 2014年

44

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

35
我看不到在setInterval的情况下如何不跟踪计时器ID。它只是退出功能。
Kekoa

1
另一种选择setTimeout是,不保存id,而是添加一条if语句,仅在某些条件为true时设置下一次超时。
nnnnnn

7
Kekoa,好点。但是您只需要保存一次间隔ID。超时ID可能随每次调用而变化,因此您必须跟踪这些变化。想要清除超时的代码必须以某种方式访问​​此变量的当前值。另外,doStuff由于id无效,因此无法清除运行时的超时。但是,并没有必要保存超时ID。相反,您可以停止呼叫setTimeout
罗伯特

4
以我的经验,大多数情况下,即使使用setTimeout,您也希望能够取消。您希望在下一次调用之前(而不是在下一次调用完成之后)取消的时间有99%。
Kamiel Wanrooij 2011年

23

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

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

myTimeoutFunction();

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


20

它们的目的非常不同。

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


7
简洁明了的解释,但请注意,OP了解OP提供的示例中的基本区别。特别要注意的是,在OP的setTimeout()例子,setTimeout()被称为递归,而setInterval()不是。
DavidRR 2014年

14

当您在setInterval内运行某些功能时,该功能的工作时间超过timeout->,浏览器将被卡住。

-例如,doStuff()需要1500秒。要执行并执行:setInterval(doStuff,1000);
1)浏览器运行doStuff()耗时1.5秒。被执行;
2)〜1秒后,它将尝试再次运行doStuff()。但是以前的doStuff()仍然会执行->因此浏览器将此运行添加到队列中(先完成后运行)。
3,4,..)将相同内容添加到下一个迭代的执行队列中,但是上一个迭代的doStuff()仍在进行中...
结果,浏览器被卡住了。

为防止此行为,最好的方法是在setTimeout中运行setTimeout以模拟setInterval
要更正setTimeout调用之间的超时,可以使用JavaScript的setInterval技术的自更正替代方法。


1
我不知道为什么没人在这个答案中发现任何价值!
Randika Vishman

9

我使用setTimeout。

显然,不同之处是setTimeout调用了该方法一次,setInterval重复调用了该方法。

这是一篇很好的文章,解释了它们之间的区别:教程:带有setTimeout和setInterval的JavaScript计时器


2
是的,我有所不同,但是我提供的两段代码应该做同样的事情……
Damovisa 2009年

嗯,是的...我原本以为是...但是根据dcaunt和他的投票计数,发生的事情并不完全是。
Bravax

9

您的代码将具有不同的执行间隔,在某些项目(例如在线游戏)中,这是不可接受的。首先,您应该怎么做才能使代码以相同的间隔工作,您应该将“ 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毫秒的超时,结果将保持稳定。


7

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

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

1
“没有运行并行迭代的情况”-是的,这应该是不可能的。客户端JavaScript具有单线程执行模型,因此不会同时发生任何事件(事件处理程序等)。这就是为什么在同步ajax调用期间什么都没有发生(浏览器没有响应)的原因。
MrWhite 2012年

浏览器是否在“间隔”之间的几毫秒内做出响应?如果尚有其他事件会触发吗?(感谢btw +1的测试)
MrWhite

1
根据MDN,如果该函数执行的时间长于间隔时间,则被视为“ 危险使用 ”。最好使用递归setTimout调用。
MrWhite 2012年

6

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

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


5

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

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

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


4

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

3

嗯,正如我刚刚了解到的,setTimeout在一种情况下更好。我总是使用setInterval,我已经在后台运行了超过半小时。当我切换回该选项卡时,幻灯片放映(使用了代码的幻灯片)的变化非常快,而不是每5秒钟更改一次。实际上,随着我对其进行更多的测试,它确实会再次发生,并且是否是浏览器的故障并不重要,因为使用setTimeout时,这种情况是完全不可能的。


听起来好像您在所调用的函数内调用了setInterval。这就是使用setTimeout循环的方式,但是实际上,每次使用setInterval调用时,您实际上都在创建一个全新的循环。
斯蒂芬·R



-1

和setInterval(expression,duration)之间最一般的区别是

setTimeout()将仅在指定的持续时间之后执行一次表达式。

然而,

setInterval()将在每个指定的持续时间之后在INFINITE-LOOP中执行表达式。


-5

我想SetIntervalSetTimeout不同。SetInterval根据设置的时间SetTimeout执行该块,同时执行一次代码块。

在超时倒数秒后尝试以下代码集:

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

然后尝试

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

您可以自己查看差异。


这不能回答问题。settimeout()可以递归使用,以提供与setinterval相同的结果。问题在于这两个选项的性能。
Tomas Gonzalez
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.