Settimeout in foreach is not working properly

Settimeout in foreach is not working properly
0

#1
pattern.forEach( function(elem, index) {

			setTimeout(yo(elem),index*1000);
				
				function yo(elem){
					console.log(elem);
				switch (elem) {
					case 'blue':
						// statements_1
						$('#blue').css('background-color','grey');
					    bsound.play();
					    setTimeout(function(){$('#blue').css('background-color','blue');},500);
						break;
					case 'green':
						// statements_1
						$('#green').css('background-color','grey');
					    gsound.play();
					    setTimeout(function(){$('#green').css('background-color','green');},500);
						break;
					case 'red':
						// statements_1
						$('#red').css('background-color','grey');
					    bsound.play();
					    setTimeout(function(){$('#red').css('background-color','red');},500);
						break;
					case 'yellow':
						// statements_1
						$('#yellow').css('background-color','grey');
					    ysound.play();
					    setTimeout(function(){$('#yellow').css('background-color','yellow');},500);
						break;
					
				}

			}
			});

Hey, guys, I am trying to build a Simon game. Now as the game follow the computer will generate random patterns. Every color has its specific sound which will be played. Now the logic I am using here is we will loop on pattern array using foreach method. At every element, I will call settimeout callback function with index*1000. I am multiplying index with ms so there is a gap between every call. Now if I have a pattern array of say [‘red’] it works. for [‘red’,‘yellow’] It will simultaneously play both blocks sound for yellow and red and will also change the color of those blocks to gray and changing it back to their original color (problem is it is doing that simultaneously). if [‘red’,‘yellow’,‘red’] it will do the same thing as for [‘red’,‘yellow’] (meaning it skips the red color sound).
Can any body please tell me why this is happening.


#2

put the function in where you are calling the function

setTimeout(function () {
  switch(elem) {
  }
}, index*1000)

perhaps this can explain why
https://stackoverflow.com/questions/37977602/settimeout-not-working-inside-foreach


#3

Hi @airasyaqub, I’m not sure about the code inside the yo function, but the outer setTimeout wouldn’t work imo because you are passing a function call (yo(elem)) to it, rather than just a function that it requires.
You could do something like this - setTimeout(yo, index*1000, elem) instead, it would take the yo function and pass elem as argument.
(https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout)


#4
$('#box p').text(boxnumber);
			console.log(pattern);

			for(let i=0;i<pattern.length;i++){
					//const j =i;
				patfunc1=setTimeout(function(i){

				$('#'+pattern[i]).css('background-color','grey');
				if(pattern[i]=='blue'){
          	bsound.play();
       	 	}
        	else if(pattern[i]=='green'){
          	gsound.play();
       		 }
        	else if(pattern[i]=='red'){
          	rsound.play();
       		 }
        	else if(pattern[i]=='yellow'){
          	ysound.play();
       		 }

       		 setTimeout(function(){$('#'+pattern[i]).css('background-color',pattern[i]);},500);




				},i*1000);

			}

I decided to use ES6 instead of clousres to keep the scope of ‘i’ in settimeout. But its not working and niether playing anything nor changing the colors of elements. I searched on stackoverflow and it says that ‘let’ will solve the issue. but why isnt it working up here ?


#5

You are going to keep running into problems if you are calling a function inside a loop. I am not sure what you are trying to do with scoping. Let keeps scope inside blocks, that means that code outside of your forl oop can’t use i, but the settimeout inside your for loop can of course still access it.

When I remade my simon game recently, I tried to avoid the issue by not using a loop and avoiding calling settimeout normally. Async functions and recursion made it a lot easier. Here is a relevant snippet, I’ll try to give you an idea of what I did but not give the full solution away:

// num parameter represents current button
// and is for recursive calling.
// First call will be defaulted to 0
const showSeries = async (num = 0) => {
  // for every button in series
  if (num < simonSeries.length) {
    currentBtn = simonSeries[num]

    highlightStart() // changes color of button and plays sound based on currentBtn
    await delay(btnLength)
    highlightStop() // reverts to normal color

    // short delay before recursively calling next button in series
    await delay(600) // don't use a magic number here like I did in this example
    showSeries(++num)
  } else {
    // each button in the series has been highlighted
    switchUser() 
  }
}


// example call
simonSeries.push(newColor())
showSeries()

If you know how to write async functions, I think recursion is a lot better than a loop imho.


#6

You need to pass setTimeout a reference to a function, or an anonymous finction.

But instead you are passing it the result of an immediate call to yo(). You are not passing setTimeout() the function yo, you are invoking yo() immediately then passing it’s response.

That return value of yo() is undefined. After your timer count has transpired, it is trying to invoke that undefined that you gave it.


#7

In this case I think your code won’t work because the function that you pass to the setTimeout takes an
argument i that you never pass to it, so the argument evaluates to undefined, otherwise your idea to use let is good and might work. Look at these two fro loops-
let patterns = ["red", "green", "blue", "yellow"]; for(i=0; i < patterns.length; i++) { setTimeout(function() { console.log("i is" + i + " " + patterns[i])}, i* 1000).
This one will work as expected - let has a block scope, so when the function inside setTimeout is run and looks for i it will find i in it’s outer scope with the value it was assigned in that run of the for loop.

let patterns = ["red", "green","blue", "yellow"]; for(i = 0; i < patterns.length; i++) { setTimeout(function(i) { console.log("i is " + i + " " + patterns[i])}, i* 1000)
This will not work as expected. Function inside setTimeout has own local variable i so it never looks for it in the outer scope. And because I didn’t pass an argument to the function i will always be undefined.