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.

8
  • 1
    You are doing that wrong.. try to change the logic that you have to use setInterval, instead of for Commented Apr 14, 2016 at 18:22
  • I can hardly even think about this code without my head asploding. Commented Apr 14, 2016 at 18:23
  • 1
    This approach is going to create millions of timers very quickly and exhaust available resources. I doubt that's a requirement. Commented Apr 14, 2016 at 18:25
  • This is an infinite loop. It will crash the page or browser depending on OS and browser types. Commented Apr 14, 2016 at 18:27
  • 1
    What you have here is an XY problem. You created an infinite loop to do some work, but have not shown what work needs an infinite loop to be accomplished. Commented Apr 14, 2016 at 18:28

5 Answers 5

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);

Sign up to request clarification or add additional context in comments.

4 Comments

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, setTimeout wants a function (or string?) as its first parameter, which bind gives us. Where are you suggesting to put the call?
@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.
@flatline I was going to ask this here, but ... duh... stackoverflow.com/questions/36632000/…
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);

2 Comments

If you really want to stop it, setTimeout returns a handle that can be passed to clearTimeout.
@flatline Thanks, added :)
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>

Comments

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.

Comments

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

5 Comments

It will loop infinitely, not "about a billion times", before the first setTimeout's callback runs; as a consequence the callback will never run.
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.
@I'm not talking about your code, I'm talking about the statement at the top of your answer regarding the OP's code.
ah, well, I'd like to understand why my code was downvoted by whoever voted it down.
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].

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.