1

I have this code in ES6:

function loopLettersEvery3Seconds() {
    const letters = ['a', 'b', 'c'];

    let offset = 0;
    letters.forEach((letter, index) => {
      setTimeout(() => {
        // I do something with the letter, nothing important
      }, 3000 + offset);
      offset += 3000;
      if (index === letters.length - 1) {
        loopLettersEvery3Seconds(letters);
      }
    });
}

This function loop over an array of letters and every 3 second I can do something with every letter. But the thing is that when this loop comes to an end and finish, I can't repeat again the process... The recursive call generates an stack overflow (Uncaught RangeError: Maximum call stack size exceeded) lol

Tell me how to do it! Thanks!!

BR

4 Answers 4

3

You can do something like this. This is how you can simulate a setInterval via recursion and setTimeouts

var array = ['a', 'b', 'c']

function loopLettersEvery3Seconds(arr, index) {
    if (index === arr.length) return;
    //Do stuff with array
    setTimeout(loopLettersEvery3Seconds, 3000, arr, index+1)
}
loopLettersEvery3Seconds(array, 0)

This way, you don't have to deal with the synchronous nature of loops and instead, do every thing based on the callstack and the web api (is that the right term for timeouts?)

If you want it to loop forever, then do this

var array = ['a', 'b', 'c']

function loopLettersEvery3Seconds(arr, index) {
    if (index === arr.length) return loopLettersEvery3Seconds(arr, 0);

    setTimeout(loopLettersEvery3Seconds, 3000, arr, index+1)
}
loopLettersEvery3Seconds(array, 0)
Sign up to request clarification or add additional context in comments.

1 Comment

I think this is a more elegant way of doing the recursion. If you are curious, here is "your" code: github.com/Ismaestro/ismaelramos/commit/…
1

forEach is synchronous. You're doing your recursive call immediately after setting up the last timeout, which obviously will lead to a stack overflow when the function is calling itself without a base case to stop at.

For an forever-running animation, you will want to put the "recursive" call inside the last timeout:

function loopLettersEvery3Seconds() {
  ['a', 'b', 'c'].forEach((letter, index) => {
    setTimeout(() => {
      // I do something with the letter, nothing important
      if (index === letters.length - 1) {
        loopLettersEvery3Seconds(letters);
      }
    }, 3000 * (index + 1));
  });
}

Comments

0

You should probably be using setInterval instead of setTimeout, which allows you to avoid using an offset variable. Note that you can use the modulo (%) operator to make sure that your index "loops" as you increment it:

function loopLettersEvery3Seconds() {
    const letters = ['a', 'b', 'c']
    let index = 0
    
    setInterval(() => {
      console.log(letters[index])
      index = (index + 1) % letters.length
    }, 3000)
}

loopLettersEvery3Seconds()

5 Comments

@SeanKwon OP doesn't really want it to stop it appears
@Bergi Woops, spoke too soon then.
@Bergi added infinite loop function
could you explain (verbosely) how the modulo (remainder) operator exactly works here? see also: stackoverflow.com/a/50339293/2153622
(index + 1) % letters.length here does the same thing as (index + 1) < letters.length ? (index + 1) : 0
0

I guess a quasi recursive loop is best in this case;

var chars = ["a","b","c"];

function loop(a,i = 0){
  console.log(a[i%a.length]);
  setTimeout(loop,1000,a,++i);
}

loop(chars);

Comments

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.