setTimeout loop

I have tried every solution on stack overflow for this. I have an array of values. I want to run a start function on array[i], then after a delay run an end function on array[i]. I want to do this for every element in array with a delay between each iteration. Any suggestions @P1xt, @PortableStick, or others?
Here’s an example of something I want:

var fruits = ['Apple', 'Cherry', 'Orange']; 

var i = 0; 
function delayedLoop() {
  document.getElementById('output').innerHTML = 'Starting to eat ' + fruits[i];
  setTimeout(function() {
    document.getElementById('output').innerHTML = 'Ate ' + fruits[i];
  }, 1000);
  
  if (++i == fruits.length) {
    return;
  }
  
  setTimeout(delayedLoop, 2000);
}
delayedLoop();
document.getElementById('output').innerHTML = 'Done!';

Thanks I knew I could count on you! I’m on vacation in Montana right now and can’t check if it works and will be out in the woods, but I’m sure it will help. Thank you for the quick reply!

I think this is the same problem one has when making the animation sequence for the simon game.
The following code was how I solved it. I generalized the code a little. Hope it helps

/** Process element with setTimeout then callback */
function processElement(element, onFinished) {
    setTimeout(function() {

        /* do stuff with element */

        if (onFinished !== undefined) {
            onFinished();
        }

    }, 2000);
}

/** queue up processing by recusively setting the finished-callback to the next iteration */
function processSequence(sequence, onSequenceFinished) {
    if (sequence.length === 1) {
        processElement(sequence[0], function() {
            if (onSequenceFinished !== undefined) {
                onSequenceFinished(); // call sequenceFinished callback when all is done
            }
        });
    } else {
        processElement(sequence[0], function() {
            processSequence(sequence.slice(1), onSequenceFinished); // do next step in the loop by chopping off first element
        });
    }
}
1 Like

this is an article fcc had an email about recently that i think might be relevant to your problem. the article itself doesn’t provide the best explanation of the issues it brings up, but the first comment by Christian Ferrier does.

basically, because of the way js enclosures work,
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
outputs 10, 10 times, because the loop finishes first, leaving i=10 by the time any of the console.logs run. an inner function to create a new closure or using let instead of var can help.

specifically, if you wrap your settimeout in a self-invoking function as so, it will pass a copy of i instead of a reference to it, which will resolve your ordering, except for the fact that “done” will land in the middle because it ends up executing before the settimeouts run.

(function(i) {
setTimeout(function() {
document.getElementById(‘output’).innerHTML = 'Ate ’ + fruits[i];
}, 1000);
})(i);

1 Like

Lol I just helped someone with a simon game a few days ago, you’re right, it’s very similar. My simon code there didn’t work for this, so thanks for your code! What goes around comes around xD.

I would recommend using forEach()

My submission isn’t much distinct from @P1xt’s first one (stylistic differences, really), which I think it’s about the best we can do with vanilla JS.

var fruit = ["Apples", "Banaynays", "Oranges"];

function DelayedMunch(arr, num) {
  if(!num) {
    num = 0;
  }
  
  if(num < arr.length) {
    var message = document.getElementById('message');
    message.innerHTML = 'Starting to eat ' + arr[num];
    setTimeout(function() {
      message.innerHTML = 'Eated ' + arr[num];
      setTimeout(function() {
        DelayedMunch(arr, ++num);
      }, 1000);
    }, 1000)
  } else {
    return;
  }
}

DelayedMunch(fruit);

http://codepen.io/PortableStick/pen/ZpZKKk?editors=0010

I hate setTimeout. I don’t know if I should, but I do. It’s not so much a code smell as it is a gut-wrenching code stench. It’s difficult to read and we quickly descend into Callback Hell. Generators or observables would be my tool of choice here, but they’re not as readily available. Oh well.

@GreggDurishan, closures are where my mind first went to as well. Any answer having to do with iteration and asynchronous execution, like your example, will rely on a closure. The difference here, though, is that we can execute the code synchronously, meaning we don’t change execution scope and fruits[i] will always be what we expect it to be.

2 Likes

This seems to work the best and I got it working easily. However I don’t understand the DelayedMunch(++num);.
I changed it to num++ which I have always seen and used and it did not work.

I agree, I only wanted to try to provide him some insight into what was going wrong in his original approach; I felt he had already been given suggestions that fulfilled his purpose, by avoiding rather than debugging his problems.

2 Likes