Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用javascript的无限计时器循环(无设置间隔)?_Javascript_Performance_Memory Leaks - Fatal编程技术网

使用javascript的无限计时器循环(无设置间隔)?

使用javascript的无限计时器循环(无设置间隔)?,javascript,performance,memory-leaks,Javascript,Performance,Memory Leaks,(一位朋友)让我建立一个计时器(无限计时器,每秒写一行),但没有setInterval 我希望: var i = 0; function k(myId, cb) { setTimeout(function () { console.log(myId); cb(); }, 1000); } function go() { i++;

(一位朋友)让我建立一个计时器(无限计时器,每秒写一行),但没有
setInterval

我希望:

var i = 0;

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

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

    go();
它正在发挥作用

问题是我担心会有记忆压力。它实际上创建了一个递归,过了一段时间(一周或几周),这个过程将消耗大量内存。(堆栈永远不会释放)


如何更改代码以减少内存消耗?

这不会造成内存泄漏

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

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毫秒的中断,无论函数执行多长时间

有关此模式的更多信息,请参阅。

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

setTimeout的工作方式是它立即返回。因此,对
k
的调用立即结束,堆栈被解除分配

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

*注意:这里我没有使用ECMAScript规范中定义的范围的严格含义。我的意思是,对
k
的调用将如同在普通的
标记中编写一样:也就是说,在任何其他函数调用之外

关于你对关闭的关注 在您的特定情况下,
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
    }
可能更简单 我应该注意到你的代码相当复杂。如果您只是这样编写,它可以编写得更简单,并且完全不使用闭包(减去全局变量i):

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

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

它不会创建递归,因为函数完全退出,然后再次调用

递归堆栈彼此重叠

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
只要你愿意,浏览器就有机会“呼吸”。有机会更新屏幕,处理其他事件。使用正确的术语,它不会阻止浏览器。

“范围”属于函数的词法上下文,而不是调用堆栈。更好的措辞可能是,“当超时实际发生并且对
go
的调用再次发生时,它以一个新的空堆栈开始。它不会继续添加到前一个堆栈。”“但是从全局范围”-这不太正确。上次对
k
的调用的结束情况如何?传递给
setTimeout()
的匿名函数使用了
k()
中的一个参数,因此,即使
k()
已经完成了执行,它的一些部分仍然会挂起至少一段时间…@nnnnnn你能详细说明一下吗?@Lee:阐明了我的意思。我没有使用你的措辞,因为虽然措辞在技术上是错误的,但我发现更多的人通过这种方式更快地理解这个概念。@RoyiNamir:阅读我的答案:。它是关于XMLHttpRequest而不是setTimeout的,但是有一个很好的ASCII艺术图,解释了事件循环在浏览器中的工作原理,也适用于setTimeout。“我担心会有内存压力”-你担心可能会有,或者你已经测试过了,结果有?你提到要运行一周-你真的这么做了并且有问题吗?正如下面的答案中提到的,这不是递归。另外,为什么要用
go()
k()
和匿名函数设置三重函数?您可以在
go()
中执行
console.log()
,然后执行
setTimeout(go,1000)
。请注意,OPs
setTimeout
调用中的函数表达式创建了外部函数执行上下文的闭包,并显式地创建了
cb
,因此形成了一个作用域堆栈。它可能会被优化,因为唯一需要的封闭是直接的外部和全局环境,但这取决于实现,可能不可靠。无论如何,你是对的,它不应该产生任何问题。回答得好。我喜欢
a();a();a()
example@RoyiNamir-传递给
setTimeout
的匿名函数、外部
k
函数和全局对象之间有一个闭包。但是当匿名函数被调用并完成执行时,它就(或应该)可用于垃圾收集。新的
setTimeout
中的新函数形成了一个新的闭包,但它不涉及上一个闭包,它是一个全新的执行上下文。@RobG你能加入吗?@RobG--完全同意更接近的问题。然而,出于简单的考虑,我在回答中忽略了它。