使用javascript(无setInterval)的无限定时器循环?

时间:2012-11-22 06:15:08

标签: javascript performance memory-leaks

有人问我(由朋友)建立一个计时器(每秒写一行的无限计时器),但没有setInterval

solved与:

var i = 0;

    function k(myId, cb)
    {
        setTimeout(function ()
        {
            console.log(myId);
            cb();
        }, 1000);
    }

    function go()
    {
        i++;
        k(i, go);
    }

    go();

它正在发挥作用。

问题在于我担心会有内存压力。它实际上创建了一个递归,并在一段时间后(一周或某事) - 该过程将消耗大量内存。 (堆栈永远不会被释放)

如何更改代码以免耗费大量内存?

3 个答案:

答案 0 :(得分:13)

这不是递归

它可能看起来像递归,但是setTimeout不会创建递归。

setTimeout的工作方式是它立即返回。因此,对k的调用会在其堆栈被释放后立即结束。

当超时实际发生且对go的调用再次发生时,它不是从前一次调用k开始,而是来自全局范围*。

*注意:我没有使用ECMAScript规范中定义的范围的严格含义。我的意思是对k的调用就好像你用普通的<script></script>标签写的那样:也就是说,在任何其他函数调用之外。

关于您对关闭的关注

在您的特定情况下,k函数创建的闭包中实际包含的内容很少。唯一重要的闭包是对参数cbmyId的引用。即便如此,它只持续大约一秒钟:

 #1   function k(myId, cb) {
 #2        setTimeout(function(){
 #3            console.log(myId); // there is a closure here to myId
 #4            cb();              // and another one for cb
 #5
             /* But at this point in the function, setTimeout ends
             * and as the function returns, there are no remaining
             * references to either "cb" or "myId" accessible
             * anywhere else. Which means that the GC can immediately
             * free them (though in reality the GC may run a bit later)
             */
  #6       }, 1000); // So one second is roughly the longest the closure lasts
    }

可能更简单

我应该注意到你的代码相当复杂。它可以写得更简单,如果你只是这样写的话,根本不使用闭包(减去全局变量i):

// Simpler, does exactly the same thing:
var i = 0;
function go () {
    console.log(i);
    i++;
    setTimeout(go, 1000); // callback
}
go();

答案 1 :(得分:6)

这一行是假的:

  

它实际上创建了一个递归,经过一段时间(一周或某事) - 该过程将消耗大量内存。 (堆栈永远不会被释放)

创建递归,因为该函数完全退出 ,然后再次调用。

递归堆叠在彼此之上

function a() {a()}; // function calls itself until a stack overflow.

堆栈看起来像这样

a()
  a()
    a()
      a() ... until a crash.

使用setTimeout,您可以执行一个函数。该函数设置一个事件让函数再次运行 - 但这里有一个重要的区别:函数退出,完全,并且已经消失[1]。然后它再次被召唤。

执行明智,与此相比没有太大区别:

function a() {console.log("I am called");}

a(); // Call the function;
a(); // Call the function again
a(); // Call the function again

setTimeout只是让浏览器有机会“呼吸”,如果你愿意的话。屏幕更新的机会,其他事件要处理。使用正确的术语,block浏览器不会。

答案 2 :(得分:2)

这不会造成内存泄漏。

事实上,这是一个非常常用的概念。通常它以这种形式出现:

setTimeout(function next() {

    // Do something...

    // Have the function set another timeout to call itself later.
    setTimeout(next, 10);

}, 10);

如果要检查某些内容(此处每10毫秒),最好使用此模式而不是setInterval,因为它可以提高页面性能。例如,如果您的函数执行时间超过10毫秒,并且您使用setInterval(f, 10)则会不断调用它。但是,如果您使用上面的setTimeout模式,那么无论您的函数执行多长时间,它都至少会确保处理器在每次调用之间间隔10毫秒。

有关此模式的详细信息,请参阅this video (starting at 7:46) from Paul Irish