Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

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?

like image 742
Royi Namir Avatar asked Nov 22 '12 06:11

Royi Namir


2 Answers

It's not recursion

It may look like recursion, but setTimeout does not create recursion.

The way setTimeout works is that it returns immediately. So the call to k ends immediately with its stack deallocated.

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*.

* Note: I'm not using the strict meaning of scope as defined in ECMAScript spec here. 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.

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. The only significant closure is the reference to the arguments cb and myId. 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:

// Simpler, does exactly the same thing:
var i = 0;
function go () {
    console.log(i);
    i++;
    setTimeout(go, 1000); // callback
}
go();
like image 190
slebetman Avatar answered Oct 20 '22 01:10

slebetman


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. 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]. 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. A chance for the screen to update, other events to process. It doesn't, to use the right terminology, block the browser.

like image 30
Jeremy J Starcher Avatar answered Oct 20 '22 00:10

Jeremy J Starcher