【问题标题】:Building a simon game and can't get one function to completely finish before another function runs构建一个 simon 游戏并且在另一个函数运行之前无法让一个函数完全完成
【发布时间】:2017-11-21 02:51:55
【问题描述】:

在我的 correctClick 函数中,我将按钮单击与一个数组 (buttonPressValidate) 进行比较,该数组 (buttonPressValidate) 包含屏幕上突出显示的所有按钮的 id。如果单击突出显示的按钮,则 replayFlash 函数会突出显示 buttonPressValidate 中的所有按钮,然后在 replayFlash 函数完成后 highLightSquare 函数应该高亮一个新的正方形。我遇到的问题是我的 highLightSquare 函数在突出显示新方块之前没有等待 replayFlash 函数完成。

    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);
      }
}

【问题讨论】:

  • 西蒙经常出现。这是一些编程课程的练习吗?
  • 是的,它是 FreeCodeCamp 课程的最后一个高级项目。
  • 你能告诉我们你的 replayFlash 功能吗?例如,如果它还调用 setTimeout,则代码将继续执行,因为 setTimeout 是异步的。
  • 我在帖子中添加了replayFlash功能
  • 课程是否包含 Promises?

标签: javascript jquery


【解决方案1】:

setTimeout 的美妙之处在于它是非阻塞的(即异步),因此您的应用可以在计时器关闭之前执行其他操作。

除了当您不希望在时间结束之前继续执行时 setTimeout 有时也会出现问题。您的 replayFlash 函数在播放序列之前返回,因为 setTimeout 不会阻塞。代码的执行会一直持续到 setTimeout 间隔结束。

您需要一个同步/阻塞计时器。 already on SO (click here to view) 就是一个例子。下面我重新编写了您的 replayFlash 函数(未经测试)以使用这种阻塞等待模式。希望这将为您指明正确的方向。您还需要以类似的方式修改您的 correctClick 函数。

// 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);
  }
}

编辑:

受上述用户 Roamer-8888 的评论启发,我想提出另一个选择:承诺。这实际上是 IMO 的“正确”答案,但有点更高级的概念。 Promise 通过“等待”直到“实现”或“解决”未来操作的承诺,让您可以控制异步环境中代码的执行。

现在很多浏览器都支持原生的 Promise,但也有一些需要库,所以让我们看一个流行的“Q”promise 库的例子。这是一个简单的示例,说明如何使用 Promise 等到 setTimeout 超时后再进行其他操作。请注意,Q 库(来自 q.min.js)创建了用于创建 Promise 的全局变量 Q。

<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>

或者,使用 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>

【讨论】:

  • 是的,绝对可以保证,但由于 OP 已经在使用 jQuery,$(element).delay(1000).promise().then(...) 之类的东西将不再需要 eplicit setTimeout(),并且代码应该变得更加紧凑。
  • 我在使用 Promise 时遇到了同样的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-28
  • 1970-01-01
  • 2019-11-14
  • 1970-01-01
相关资源
最近更新 更多