简体   繁体   English

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

[英]Infinite Timer Loop with javascript ( no setInterval)?

I was asked (by a friend) to build a timer (infinite one which writes a line every second), but without setInterval . 有人问我(由朋友)建立一个计时器(每秒写一行的无限计时器),但没有setInterval

I solved it with : 解决了它:

var i = 0;

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

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

    go();

And it is working. 它正在发挥作用。

The problem is that I'm afraid there's gonna be a memory pressure. 问题是,我担心会有内存压力。 It actually creates a recursion and after a while (week or something) - the process will consume much memory. 它实际上创建了一个递归,并在一段时间后(一周或某事) - 该过程将消耗大量内存。 (the stack is never deallocated) (堆栈永远不会被释放)

How can I change my code in order not to be much memory consume? 如何更改我的代码以免耗费大量内存?

It's not recursion 这不是递归

It may look like recursion, but setTimeout does not create recursion. 它可能看起来像递归,但setTimeout不会创建递归。

The way setTimeout works is that it returns immediately. setTimeout的工作方式是它立即返回。 So the call to k ends immediately with its stack deallocated. 因此,对k的调用立即结束,其堆栈被释放。

When the timeout actually happens and the call to go happens again it is not from the point of the previous call to k but from the global scope*. 当超时实际发生,并调用go再次发生它是不是从以前调用点k但是从全球范围内*。

* Note: I'm not using the strict meaning of scope as defined in ECMAScript spec here. *注意:我没有使用ECMAScript规范中定义的范围的严格含义。 What I mean is the call to k will be made as if you have written it in a plain <script></script> tag: that is to say, outside of any other function calls. 我的意思是对k的调用就好像你用普通的<script></script>标签写的那样:也就是说,在任何其他函数调用之外。

Regarding your concern over the closure 关于你对关闭的担忧

In your specific case, there is very little that's actually enclosed in the closure created by the k function. 在您的特定情况下, k函数创建的闭包中实际包含的内容非常少。 The only significant closure is the reference to the arguments cb and myId . 唯一重要的闭包是对参数cbmyId And even then it only lasts for approximately one second: 即便如此,它只持续大约一秒钟:

 #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
    }

Could be simpler 可能更简单

I should note that your code is fairly convoluted. 我应该注意你的代码相当复杂。 It can be written simpler, and without using closures at all (minus the global variable i) if you simply write it like this: 它可以写得更简单,如果你只是像这样编写它,根本不使用闭包(减去全局变量i):

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

This line is false: 这一行是假的:

It actually creates a recursion and after a while (week or something) - the process will consume much memory. 它实际上创建了一个递归,并在一段时间后(一周或某事) - 该过程将消耗大量内存。 ( the stack is never deallocated) (堆栈永远不会被释放)

It does not create recursion, because the function exits completely and is then called again. 不会产生递归,因为该功能完全退出,然后再调用。

Recursion stacks on top of each other 递归堆栈在彼此之上

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

The stack looks like this 堆栈看起来像这样

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

With setTimeout, you execute a function. 使用setTimeout,您可以执行一个函数。 That function sets up a event for the function to run again -- but here is the important difference: The function exits, completely, and is gone[1]. 该函数设置一个事件让函数再次运行 - 但这里有一个重要的区别:函数退出,完全,并且已经消失[1]。 THEN it is called again. 然后又被召唤了。

Execution wise, it isn't much different from doing this: 执行明智,它与这样做没有太大区别:

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

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

setTimeout just gives the browser a chance to 'breath' if you will. setTimeout只是让浏览器有机会“呼吸”,如果你愿意的话。 A chance for the screen to update, other events to process. 屏幕更新的机会,其他事件要处理。 It doesn't, to use the right terminology, block the browser. 使用正确的术语,它不会block浏览器。

This does not create a memory leak. 这不会造成内存泄漏。

In fact, it's a pretty commonly used concept. 事实上,这是一个非常常用的概念。 Usually it appears in this form: 通常它以这种形式出现:

setTimeout(function next() {

    // Do something...

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

}, 10);

When you want to check for something often (here every 10 milliseconds), it can be better to use this pattern instead of setInterval because it can improve your page performance. 如果要检查某些内容(此处每10毫秒),最好使用此模式而不是setInterval因为它可以提高页面性能。 For instance, if your function takes more than 10 milliseconds to execute, and you use setInterval(f, 10) then it will continually be being called. 例如,如果你的函数执行时间超过10毫秒,并且你使用setInterval(f, 10)那么它将不断被调用。 However, if you use the setTimeout pattern above, it will at least make sure the processor gets a 10 millisecond break between each call, no matter how long your function takes to execute. 但是,如果您使用上面的setTimeout模式,它将至少确保处理器在每次调用之间获得10毫秒的中断,无论您的函数执行多长时间。

See this video (starting at 7:46) from Paul Irish for more information about this pattern. 有关此模式的更多信息,请参阅Paul Irish的此视频(从7:46开始)

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

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