0

I'd like to loop through an array ("sites") every 5 seconds. After 5 ("cycle_timeout") seconds, I'd like to invoke another function that loops through values of 0 to 120 in 1 second intervals. When the looper function is invoked outside of cycle, it works fine, but I'd like it to be invoked from inside cycle. When that happens, the looper "Delay" isn't preserved and the loop accelerates from 0 to 120 very quickly. How do I get looper to continue in 1 second intervals? Thanks for your help.

var i = 0;
var l = 120;
looper(); // looper runs correctly when invoked from here.
function looper() {
    var Delay = 1;
    console.log("i is ", i);
    i = i + 1;
    if (i == l) {
        i = 0;
    }
    setTimeout(looper, Delay * 1000);
}
var sites = ["a", "b"];
var lengthsites = sites.length;
var ii = 0;
cycle();
function cycle() {
    console.log("ii is ", ii);
    console.log("site is ", sites[ii]);
    var cycle_timeout = 5;
    setTimeout(cycle, cycle_timeout * 1000);
    //looper(); // looper accelerates when invoked from here.
    ii = ii + 1;
    if (ii == lengthsites) {
        ii = 0;
    }
}
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
meh9
  • 1
  • 2
  • if you write code that looks like this, you'll always have issues - indentation makes things easier to read (of course, you may be a genius and not need properly formatted code, I guess) – Jaromanda X Jun 25 '17 at 04:13
  • I couldn't help but notice when editing the formatting of your code example, that there are many missing semicolons. This can have all kinds of unexpected results, and should be fixed first, tested and if the problem still exists, we can proceed. See [this question](https://stackoverflow.com/questions/444080/do-you-recommend-using-semicolons-after-every-statement-in-javascript) - although someone just overrode my edit and "fixed" them all while I was typing, so we may never know if that was an issue. – Fred Gandt Jun 25 '17 at 04:20
  • oops - I added the missing `;` into the formatted code, because I'm an idiot :p – Jaromanda X Jun 25 '17 at 04:23
  • Please use *setInterval()* instead of *setTimeout* in order to have a function called in a repetitve fashion. Then you don't need to call *setTimeout* anymore within the function, which it calls. – Aedvald Tseh Jun 25 '17 at 05:36

1 Answers1

0

It accelerates from inside because of this:

function looper () {
  // stuff
  callLooperIn1Second();
}

function cycle () {
  // stuff
  looper();
  callCycleIn5Seconds();
}

Looper will call itself every second. Cycle will call a looper every 5 seconds, which will call itself every second. So every 5 seconds, cycle calls looper again, and it adds to the times it is scheduled to run. You add to the number of times looper gets called that second.

When you first call cycle, looper will run once per second.
After 5 seconds, it will run 2x per second.
After 10 seconds, it will run 3x per second.

cycle // call looper, which schedules every 1s
looper
looper
looper
looper
looper
cycle // call looper, which schedules every 1s
looper looper
looper looper
looper looper
looper looper
looper looper
cycle // call looper, which schedules every 1s
looper looper looper
looper looper looper
looper looper looper
looper looper looper
looper looper looper

Switching this to use setInterval would not work, because then you're just causing cycle to setInterval on looper every 5 seconds, which is slightly less code to accomplish the same problem.

I would recommend breaking your flow and scheduling code away from the thing that you actually intend to do.

let currentSecond = 0;
const totalSeconds = 120;

const sites = ["a", "b", "c"];
const totalSites = sites.length;
let siteIndex = 0;

function looper (i) {
  currentSecond = (i + 1) % totalSeconds;
}
function cycle (i) {
  const site = sites[i];
  siteIndex = (i + 1) % totalSites;
}

function triggerLoop () {
  const loopDelayMS = 1000;
  looper(currentSecond);
  setTimeout(triggerLoop, loopDelayMS);
}

function triggerCycle () {
  const cycleDelayMS = 5000;
  cycle(siteIndex);
  setTimeout(triggerCycle, cycleDelayMS);
}

function run () {
  triggerLoop();
  triggerCycle();
}

You could take this much further, of course.
You could run those things on separate intervals, rather than on recursive timeouts. That would be fine.

You could make the processes more dependent upon one another, and that's fine, too... except that you would need to add a lot more timer-management code, to figure out how far along looper is, in its counting, and what that means for cycle every time it's fired.

It doesn't look like those two counters are attached to one another in any possible way. You want one to count every second in 2 minutes, and you want the other to count every 5 seconds, for as long as there are elements in an array, and you want both of them to reset and keep going, when they hit their limit. So as far as I can tell, the timers have nothing to do with one another. It's just the fact that they're tied up in the code that has them misbehaving.

Norguard
  • 26,167
  • 5
  • 41
  • 49