繁体   English   中英

setTimeout 还是 setInterval?

[英]setTimeout or setInterval?

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

选项A:

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

myTimeoutFunction();

选项 B:

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

使用setTimeoutsetInterval有什么区别吗?

他们基本上尝试做同样的事情,但setInterval方法会比setTimeout方法更准确,因为setTimeout等待 1000 毫秒,运行函数然后设置另一个超时。 因此,等待时间实际上比 1000 毫秒多一点(如果您的函数需要很长时间才能执行,则等待时间会更长)。

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

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

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

有什么区别吗?

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

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

Timeout:

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

Interval:

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

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

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

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

因此,间隔尝试“赶上”以按计划进行。 但是,它们不会将一个排在彼此之上:每个间隔只能有一个待处理的执行。 (如果他们都排队,浏览器将留下一个不断扩大的未完成执行列表!)

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

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

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

你使用哪个,为什么?

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

我会考虑一次性动画的间隔,我希望尽可能流畅,而链接超时对于在页面加载时一直发生的持续动画更礼貌。 对于要求不高的用途(例如每 30 秒触发一次微不足道的更新程序或其他事情),您可以安全地使用。

在浏览器兼容性方面,setTimeout 早于 setInterval,但您今天遇到的所有浏览器都支持两者。 多年来最后一个落后者是 WinMo <6.5 中的 IE Mobile,但希望现在也落后于我们。

设置间隔()

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 可以更轻松地取消将来的代码执行。 如果您使用 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()
   -> 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 中运行某些函数时,它的工作时间比超时时间更长-> 浏览器将卡住。

- 例如, doStuff()需要 1500 秒。 执行,你做: setInterval(doStuff, 1000);
1) 浏览器运行doStuff()需要 1.5 秒。 被处决;
2) 大约 1 秒后,它尝试再次运行doStuff() 但是之前的doStuff()仍然被执行-> 所以浏览器将这个运行添加到队列中(在第一个完成后运行)。
3,4,..) 同样添加到下一次迭代的执行队列中,但前一个的doStuff()仍在进行中......
结果 - 浏览器卡住了。

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

你的代码会有不同的执行间隔,在一些项目中,比如网络游戏,这是不可接受的。 首先,你应该怎么做,让你的代码使用相同的间隔,你应该将“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 毫秒,结果将是稳定的。

我使用 setTimeout。

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

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

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

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

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

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

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

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

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

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

控制台中的区别很明显:

在此处输入图片说明

只需添加已经说过的内容,但代码的 setTimeout 版本也将达到Maximum call stack size ,这将阻止其运行。 由于递归函数没有基本情况可以停止,所以你不能让它永远运行

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

Serg Hospodarets 在他的回答中提到了这个问题,我完全同意他的评论,但他没有提到内存泄漏/垃圾收集问题。 我也遇到了一些冻结,但内存使用量很快就达到了 4 GB,用于一些微不足道的任务,这对我来说真的很糟糕。 因此,我认为这个答案对我这种情况的其他人仍然有益。 我会把它放在评论中,但缺乏这样做的声誉。 我希望你不要介意。

选项 A选项 B看起来工作相同的原因主要是因为setIntervalsetTimeout函数的位置。

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

myTimeoutFunction();

这是一个递归函数,如果doStuff很复杂, setTimeout必须跟踪所有调用setTimout加上当前doStuff ,这使得它变得越来越

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

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

那么你应该使用哪一个?

从以上内容,您应该能够得出结论,更好的是setInterval

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

当长时间使用小间隔时,差异会很明显。

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

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

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

然后尝试

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

您可以自己看到差异。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM