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

Roy*_*mir 10 javascript performance memory-leaks

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

解决了它:

var i = 0;

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

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

    go();
Run Code Online (Sandbox Code Playgroud)

它正在发挥作用.

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

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

sle*_*man 13

这不是递归

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

setTimeout的工作方式是它立即返回.所以调用k立即以其堆栈释放结束.

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

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

关于你对关闭的担忧

在您的特定情况下,实际包含在k函数创建的闭包中的内容非常少.唯一重要的闭包是参数cb和参数myId.即便如此,它只持续大约一秒钟:

 #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
    }
Run Code Online (Sandbox Code Playgroud)

可能更简单

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

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


Jer*_*her 6

这一行是假的:

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

它并没有创造递归,因为该函数退出完全,然后再调用.

递归堆栈在彼此之上

function a() {a()}; // function calls itself until a stack overflow.
Run Code Online (Sandbox Code Playgroud)

堆栈看起来像这样

a()
  a()
    a()
      a() ... until a crash.
Run Code Online (Sandbox Code Playgroud)

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

执行明智,它与这样做没有太大区别:

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

a(); // Call the function;
a(); // Call the function again
a(); // Call the function again
Run Code Online (Sandbox Code Playgroud)

setTimeout如果你愿意,只是给浏览器一个"呼吸"的机会.屏幕更新的机会,其他事件要处理.block浏览器使用正确的术语并不是这样.