0

Inside my correctClick function I'm comparing a button click to an array (buttonPressValidate) that contains all the id's of buttons that were highlighted on the screen. If the button that was highlighted is clicked then replayFlash function highlights all the buttons inside buttonPressValidate and then the highLightSquare function should highLight a new square after the replayFlash function finishes. The problem I'm having is that my highLightSquare function isn't waiting for the replayFlash function to finish before highlighting a new square.

    var clickNum = 0;
function correctClick(buttons) {
      $(".button").on("click", function() {
        var thisClick = $(this).attr("id");
        var matchBut = buttonPressValidate[clickNum];
        if(thisClick == matchBut) {
          clickNum++;
          setTimeout(function() {
            replayFlash(buttonPress);
            if(buttonPressValidate.length === clickNum) {
               setTimeout(function() {
                 highLightSquare(simonButtons);
               }, 1500);
            }
          }, 1500);
        }  
      });  
}

function replayFlash(butPressArr) {
      function eachColor(i) {
        var litColor = $(butPressArr[i]);
        setTimeout(function() {
          litColor.addClass("lit");
          if(litColor.attr("id") === "greenButton") {
             greenButton.play();
          } else if(litColor.attr("id") === "redButton") {
              redButton.play();        
          } else if(litColor.attr("id") === "blueButton") {
              blueButton.play();      
          } else if(litColor.attr("id") === "yellowButton") {
              yellowButton.play();      
          }
          setTimeout(function() {
            litColor.removeClass("lit");
          }, 1000 - (1000 / 3));
        }, 1000 * (i + 1));
      }
      for(var i = 0; i < butPressArr.length; i++) {
        eachColor(i);
      }
}
Yanter
  • 1
  • 1
  • Simon comes up quite regularly. Is it an exercise from some programming course? – Roamer-1888 Nov 21 '17 at 03:00
  • Yes its the last advanced project on the FreeCodeCamp curriculum. – Yanter Nov 21 '17 at 03:01
  • Can you show us your replayFlash function? If, for example, it also calls setTimeout, the code will continue on since setTimeout is asynchronous. – Eric Nov 21 '17 at 03:04
  • I added the replayFlash function to the post – Yanter Nov 21 '17 at 03:10
  • Did the course include Promises? – Roamer-1888 Nov 21 '17 at 03:59
  • You appear to be struggling with `correctClick()`. Maybe start by considering a better name for the function - ie a name that reflects what the function does/returns rather than the circumstance under which it is called. That approach tends to encourage you into better functional division, and makes the code easier to read. – Roamer-1888 Nov 22 '17 at 03:54
  • How's this going? – Roamer-1888 Nov 28 '17 at 23:22
  • Its good I finished the project thanks for checking in. Claimed my front end certification on FreeCodeCamp :). – Yanter Nov 30 '17 at 00:01
  • Well done. I had a crack at Simon using promises throughout. Best not to post my code as it would probably devalue the course for others. – Roamer-1888 Nov 30 '17 at 02:08

1 Answers1

0

The beauty of setTimeout is that it is non-blocking (i.e., asynchronous) so your app can do other things until the timer goes off.

Except that is also sometimes the problem with setTimeout when you don't want execution to continue until time is up. Your replayFlash function returns before the sequence is played because setTimeout does not block. Execution of the code just continues until the setTimeout interval is up.

You need a synchronous/blocking timer. An example of that is already on SO (click here to view). Below I have re-written your replayFlash function (untested) to use this blocking wait pattern. Hopefully this will point you in the right direction. You would also need to revise your correctClick function in a similar way.

// from @thinkbonobo's answer here:
// https://stackoverflow.com/questions/6921895/synchronous-delay-in-code-execution

function wait(ms) {
    var start = Date.now(),
        now = start;
    while (now - start < ms) {
      now = Date.now();
    }
}

function replayFlash(butPressArr) {
  function eachColor(i) {
    var litColor = $(butPressArr[i]);
    wait(1000 * (i + 1));
    litColor.addClass("lit");
    if(litColor.attr("id") === "greenButton") {
       greenButton.play();
    } else if(litColor.attr("id") === "redButton") {
        redButton.play();        
    } else if(litColor.attr("id") === "blueButton") {
        blueButton.play();      
    } else if(litColor.attr("id") === "yellowButton") {
        yellowButton.play();      
    }
    wait(1000 - (1000/3));
    litColor.removeClass("lit");
  }
  for(var i = 0; i < butPressArr.length; i++) {
    eachColor(i);
  }
}

EDIT:

Inspired by a comment from user Roamer-8888 above, I wanted to bring up another option: promises. This is actually the "right" answer IMO, but is a bit of a more advanced concept. Promises give you control over execution of your code in an asynchronous environment by "waiting" until a promise of future actions is "fulfilled" or "resolved".

Many browsers now support promises natively, but a few require a library, so let's see an example with the popular "Q" promise library. Here is a trivial example of how to use promises to wait until setTimeout times out before proceeding with other things. Note that the Q library (from q.min.js) creates the global variable Q that you use to create promises.

<script src='//cdnjs.cloudflare.com/ajax/libs/q.js/0.9.2/q.min.js'></script>
<script>
function myPatientFunction(message) {
  var deferred = Q.defer();
  setTimeout(function() {
    console.log(message);
    deferred.resolve();
  }, 1000)
  return deferred.promise;  
}


myPatientFunction("Hello world")
.then(function() {
  console.log("See? I waited my turn")
})
</script>

Or, using native promises that most browsers support:

<script>
function myPatientFunction(message) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      console.log(message);
      resolve();
    }, 1000)
  });  
}


myPatientFunction("Hello world")
.then(function() {
  console.log("See? I waited my turn")
})
</script>
Eric
  • 1,691
  • 16
  • 24
  • 1
    Yes, definitely promises but as OP is already using jQuery, stuff like `$(element).delay(1000).promise().then(...)` will do away with the need for eplicit `setTimeout()` and the code should become significantly more compact. – Roamer-1888 Nov 21 '17 at 18:36
  • I'm running into the same problem using promises – Yanter Nov 21 '17 at 20:38