0

I'm trying to create multiples setIntervals and store them (and clear later), but when I do this, the last setInterval override the previous, executing one time for each previous setInterval , but with the same content.

Code snippet with the weird behaviour :

var timeoutFunctions= {};

function log_on_console(text){
  console.log(' > > > inside function : '+text)
}

$( document ).ready(function() {
    for (i = 0; i < 5; i++) {
      console.log(' > > > before function : '+i)
      timeoutFunctions[i] = setInterval(function(){log_on_console(i)}, 2000);
    }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

the output on console is :

" > > > before function : 0" js:21:6
" > > > before function : 1" js:21:6
" > > > before function : 2" js:21:6
" > > > before function : 3" js:21:6
" > > > before function : 4" js:21:6

" > > > inside function : 5" (5) js:16:2 //(this one is the "problem")

I was expexcting something like this instead :

" > > > before function : 0" js:21:6
" > > > before function : 1" js:21:6
" > > > before function : 2" js:21:6
" > > > before function : 3" js:21:6
" > > > before function : 4" js:21:6

" > > > inside function : 0" js:16:2 
" > > > inside function : 1" js:16:2 
" > > > inside function : 2" js:16:2 
" > > > inside function : 3" js:16:2 
" > > > inside function : 4" js:16:2 

so, why is the last setInterval(function(){log_on_console(i)}, 2000) overriding the previous four?

Rod
  • 754
  • 11
  • 21

2 Answers2

5

The function here:

timeoutFunctions[i] = setInterval(function(){log_on_console(i)}, 2000);

...has an enduring reference to the i variable, not a copy of its value as of when the function was created. So it uses the value i has as of when it's called.

If you wanted to burn the value of i into the function as of when you create the function, you could use Function#bind:

timeoutFunctions[i] = setInterval(function(val){log_on_console(val)}.bind(null, i), 2000);

or more directly as your anonymous function there is just calling log_on_console:

timeoutFunctions[i] = setInterval(log_on_console.bind(null, i), 2000);

Function#bind returns a function that, when called, will call the original function with this set to the first argument you give bind, and passing along any additional arguments. Since your function isn't using this, I just used null for that arg. E.g.:

function foo(a) {
    console.log("a = " + a);
}
var f = foo.bind(null, 1);
f();

shows us:

a = 1

Side note: Your code is falling prey to The Horror of Implicit Globals because you don't declare i anywhere.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

use closure()

$(document).ready(function () {
    for (var i = 0; i < 5; i++) {
        console.log(' > > > before function : ' + i)
        (function (curent) {
            timeoutFunctions[curent] = setInterval(function () {
                log_on_console(curent)
            }, 2000);

        })(i)
    }
});
Balachandran
  • 9,567
  • 1
  • 16
  • 26
  • 1
    I can think of two reasons: 1. They were confused, as many will be, by the various different meanings of `i`, which is why I always use a different name for the arg. 2. Code dumps without explanation are less than useful. *Edit* Appears they've retracted the downvote, though. Also, creating extra throw-away functions in a loop isn't a great idea, either. – T.J. Crowder Mar 16 '15 at 13:31
  • 1
    @mplungjan yes ..you are correct – Balachandran Mar 16 '15 at 13:34
  • @T.J.Crowder I must admit I finally grok this closure better than your bind – mplungjan Mar 16 '15 at 13:36
  • 1
    @mplungjan: I should add a sentence of explanation about that. *Edit* Done – T.J. Crowder Mar 16 '15 at 13:36