2

I have this Javascript code.

var headlineStore = [
    function headline1(){
       //irrelevant code
    },
    function headline2(){
       //irrelevant code
    },
    function headline4(){
       //irrelevant code
    },
    function headline4(){
       //irrelevant code
    },
    function headline5(){
       //irrelevant code
    }
]


 for(var i = 0; i < headlineStore.length; i++){  //for loop to loop through array named headlineStore

     if(i == 4) //if condition to reset the counter once it reaches 5
     {
         i = 0;
     }

     (function(i){
         setTimeout(function(){
             headlineStore[i]();
         }, 5000  * i);
     }(i)); //Will load each function in the array with timeout increments 
 }  

What I have here is a for loop that loops through an array filled with functions. On every iteration a function from the array is retrieved and executed with time intervals.

What I want is that after the last function is retrieved it loops through the array again starting with the first function and will do this infinitely.

What I tried is resetting the counter when it reaches 4 but it goes out of the loop and continue executing, then for some reason the page become unresponsive.

user3703944
  • 71
  • 3
  • 9

5 Answers5

4

You need to wait until the last one executes before setting the next timeout:

var headlineStore = [
    function headline1(){
       document.write('1');
    },
    function headline2(){
       document.write('2');
    },
    function headline3(){
       document.write('3');
    },
    function headline4(){
       document.write('4');
    },
    function headline5(){
       document.write('5');
    }
]

function nextHeadline(index) {
  headlineStore[index]();
  window.setTimeout( 
    nextHeadline.bind(undefined, (index + 1) % headlineStore.length), 1000 );  
}

nextHeadline(0);
flatline
  • 42,083
  • 4
  • 31
  • 38
  • props for use of bind to avoid creating another function just to supply an argument, but why bind vs apply or call, since you don't care about your context anyway? – Tibrogargan Apr 14 '16 at 18:57
  • 2
    @Tibrogargan, `setTimeout` wants a function (or string?) as its first parameter, which `bind` gives us. Where are you suggesting to put the `call`? – flatline Apr 14 '16 at 19:03
  • 3
    @Tibrogargan In modern browsers (IE 10 and up), you can also do `setTimeout( nextHeadline, 1000, (index + 1) % headlineStore.length );` instead of using bind, as `setTimeout` now accepts additional arguments to pass to the function. – Paul Apr 14 '16 at 19:18
  • @flatline I was going to ask this here, but ... duh... http://stackoverflow.com/questions/36632000/what-is-the-difference-between-the-return-from-bind-and-the-result-of-using-func – Tibrogargan Apr 14 '16 at 19:19
  • @Paulpro Thanks, added this to my answer. – Tibrogargan Apr 14 '16 at 19:25
2

I think this is more along the lines of what you need (call 1 function every 5 seconds). Support for this use of setTimeout depends on the browser

var timeoutHandle = undefined;

headlineStore =
[ function1
, function2
, function3
  ...
];

function showHeadline(idx) {
    headlineStore[idx](idx);
    timeoutHandle = setTimeout(showHeadline, 5000, (idx+1)%headlineStore.length);
}

showHeadline(0);
Tibrogargan
  • 4,508
  • 3
  • 19
  • 38
1

You can utilize .queue() , .promise()

var headlineStore = [
  function headline1(next) {
    //irrelevant code
    console.log("headline1");
    setTimeout(next, 5000);
  },
  function headline2(next) {
    //irrelevant code
    console.log("headline2");
    setTimeout(next, 5000);
  },
  function headline3(next) {
    //irrelevant code
    console.log("headline3");
    setTimeout(next, 5000);
  },
  function headline4(next) {
    //irrelevant code
    console.log("headline4");
    setTimeout(next, 5000);
  },
  function headline5(next) {
    //irrelevant code
    console.log("headline5");
    setTimeout(next, 5000);
  }
];

(function cycle() {
  return $({}).queue("headlines", $.map(headlineStore, function(headline, i) {
      return headline
    })).dequeue("headlines")
    .promise("headlines")
    .then(cycle) // loop infinitely
}());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
guest271314
  • 1
  • 15
  • 104
  • 177
1

I don't know what you are trying to achieve, but I will try explaining why your code is not working.

JS runs on single thread and any function you put in setTimeout or setInterval is queued in the event queue. Functions from this event queue are run only when the main thead on which your JS code was running is free.

To explain what I said, follow this code

(function() {

    // A function queued to be called after 200 ms but it gets called 
    // only when the main thread has finished executing this piece of 
    // code inside the IIFE 
    setTimeout(function() { 
        console.log('Intended to be called after 200 ms delay, however called after the blocking completed');
    }, 200);

    // However the main thead is intentionally blocked here for 5 sec
    // so the queued call can only happen post this IIFE has finished 
    // execution.

    console.log('About to start 5 sec blocking');   
    var now = +new Date;
    while(+new Date - now < 5e3); // simulating 5 sec blocking
    console.log('blocking complete.');

})();

To better understand the queue and related stuff, I would recommend watching What the heck is Event Loop

Now, comming back to your question, you inadvertantly created an infinite loop when you set i = 0 in the if(i == 4) block. So, ultimately, you are queuing the functions in the event queue, but not freeing the main thread. Hence the event queue never get a chance to execute.

function first() {
    console.log('hello from first function');
}

function second() {
    console.log('hello from second function');
}

function third() {
    console.log('hello from third function');
}

function fourth() {
    console.log('hello from fourth function');
}

function fifth() {
    console.log('hello from fifth function');
}

var array = [first, second, third, fourth, fifth];

for (var i = 0, len = array.length; i < len; i++) {

    if (i == 4) { i = 0;} // this causes infinite loop

    console.log('I became an infinite loop');

    // functions are added to the queue, but the main thread is stuck in an infinite loop 
    // hence cannot execute these timeouts
    (function(i) {
        setTimeout(function() {
            array[i]();
        }, 1e2*i);
    })(i);
}

Therefore, you need a way to call an array of functions on and on. This seems to me a use case for recursion. I hope that you could achieve easily.

Ravi Tiwari
  • 946
  • 8
  • 17
0

The problem is that this function will loop forever before the first timeout ever fires the first array function. You need to increment the loop within the timeout function. and make it wait.

Try this:

var headlineStore = [
    function headline1(){
       //irrelevant code
    },
    function headline2(){
       //irrelevant code
    },
    function headline3(){
       //irrelevant code
    },
    function headline4(){
       //irrelevant code
    },
    function headline5(){
       //irrelevant code
    }
]


var i = 0;

function loopForever(){
    setTimeout(function(){
        headlineStore[i]();
        i++;
        if(i == 5){
            i = 0;
        }
        loopForever();
    }, 5000  * (i + 1)); // I added the +1, else your first function fires immediately.  seemed like you didn't want that.
}
Perry Tew
  • 2,632
  • 3
  • 22
  • 25
  • 1
    It will loop ***infinitely***, not "about a billion times", before the first setTimeout's callback runs; as a consequence the callback will never run. – Paul Apr 14 '16 at 18:36
  • downvote? for what? my code will not loop infinitely. read it again. the function runs once and exits. it gets called again at the end of the timeout. SMH. – Perry Tew Apr 14 '16 at 18:38
  • @I'm not talking about your code, I'm talking about the statement at the top of your answer regarding the OP's code. – Paul Apr 14 '16 at 18:39
  • ah, well, I'd like to understand why my code was downvoted by whoever voted it down. – Perry Tew Apr 14 '16 at 18:39
  • 1
    I did downvote, but you probably didn't deserve it, so I removed it. I would suggest changing your code to start from `i = 0` and add a `headline3` function to the array, as I think the duplicated headline4 in the OP's code was meant to be a 3 (and then make the loop reset at `i == 5` or increment i before accessing `headlineStore[i]`. – Paul Apr 14 '16 at 18:43