理解JavaScript setTimeout和setInterval

时间:2014-02-04 02:43:46

标签: javascript dom settimeout setinterval control-flow

我需要一些帮助来理解和学习如何控制这些功能来做我打算做的事情

所以基本上我来自Java背景,并通过“Pong游戏”项目深入JavaScript。我已经设法让setInteval运行游戏,每隔20ms调用我的主游戏循环,所以一切都好。但是,我正在尝试实现一种“倒计时到开始 - 循环”类型的功能,基本上可以在轮次之间显示隐藏的div,设置为innerHTML = "3" // then "2" then "1" then "GO!"

我最初尝试通过将setTimeout置于4循环for循环(3,2,1,go)中来实现此目的,但始终只显示最后一次迭代。我尝试了一点修修补补,但我一直回想起我错过了控制流程的基本概念。

我将从我的程序中发布相关代码,我的问题基本上是如何编写错误的代码,以及我需要了解的有关setTimeout和setInterval的内容以便能够修复它执行我打算的方式。我有兴趣学习如何理解和掌握这些调用,所以虽然代码示例很棒,可以帮助我理解并且显然不是不受欢迎的,但我只想说清楚我不是在找你“修复我的代码“。另外,请不要jQuery。

整个程序将是一个很大的代码墙,所以我会尽量保持它的修剪和相关性:

//this function is called from the html via onclick="initGame();"
function initGame(){

    usrScore = 0;
    compScore = 0;
    isInPlay = true;

    //in code not shown here, these objects all have tracking variables
    //(xPos, yPos, upperBound, etc) to update the CSS
    board = new Board("board");
    ball = new Ball("ball");
    lPaddle = new LPaddle("lPaddle");
    rPaddle = new RPaddle("rPaddle");

    renderRate = setInterval(function(){play();}, 20);

}

function initNewRound(){

    /*
     * a bunch of code to reset the pieces and their tracking variables(xPos, etc)    
     */

    //make my hidden div pop into visibility to display countdown (in center of board)
    count = document.getElementById("countdown");
    count.style.visibility = "visible"; 


     //*****!!!! Here's my issue !!!!*****//

    //somehow i ends up as -1 and that's what is displayed on screen
    //nothing else gets displayed except -1
    for(var i = 3; i >= 0; i--){
        setInterval(function(){transition(i);}, 1000);
    }
}

//takes initNewRound() for-loop var i and is intended to display 3, 2, 1, GO!
function transition(i){
    count.innerHTML = (i === 0) ? "Go" : i;
}

//and lastly my main game loop "play()" just for context
function play(){

    if(usrScore < 5 && compScore < 5){

        isInPlay = true;
        checkCollision();
        moveBall();
        moveRPaddle();

        if(goalScored()){
            isInPlay = false;
            initNewRound();
        }
    }

} 

非常感谢您的建议,我对JavaScript很陌生,所以我真的很感激。

2 个答案:

答案 0 :(得分:1)

扩展cookie monster的注释,当你在循环中使用setInterval时,你正在排队将在基本代码流完成后运行的方法执行。您可以将单个执行排队,并使用变量闭包或全局计数器来跟踪当前计数,而不是排队多个setInterval执行。在下面的示例中,我使用了一个全局变量:

var i = 3 // global counter;
var counterInterval = null; // this will be the id of the interval so we can stop it

function initNewRound() {
    // do reset stuff

    counterInterval = setInterval(function () { transition() }, 1000); // set interval returns a ID number
}

// we don't need to worry about passing i, because it is global
function transition() {
    if (i > 0) {
        count.innerHTML = i;
    }
    else if (i === 0) {
        count.innerHTML = "Go!";
    }
    else {
        i = 4; // set it to 4, so we can do i-- as one line
        clearInterval(counterInterval); // this stops execution of the interval; we have to specify the id, so you don't kill the main game loop
    }

    i--;
}

这是Fiddle Demo

答案 1 :(得分:0)

问题在于此代码:

for(var i = 3; i >= 0; i--){
    setInterval(function(){transition(i);}, 1000);
}

当代码运行时,它会为每个循环创建一个新函数3次,然后将该函数传递给setInterval。这些新函数中的每一个都指变量i。 当第一个新函数运行时,它首先查找名为i的局部变量(在它自己的范围内)。当它找不到它时,它会查看封闭范围,并发现我的值为-1。 在Javascript中,变量是词法范围的;内部函数可以访问包含它的范围中定义的变量。这个概念也被称为“封闭”。这可能是学习语言中最令人困惑的方面,但一旦你理解它就会非常强大。

没有必要求助于全局变量,因为您可以安全地保持在封闭范围内:

function initNewRound(){
 var i = 3;
 var count = document.getElementById("countdown");
 count.style.visibility = "visible";

 var interval = setInterval(function(){
  //this function can see variables declared by the function that created it
  count.innerHTML = i || "Go"; //another good trick
  i-=1;
  i || clearInterval(interval); //stop the interval when i is 0
 },1000);
}

每次调用此函数都会创建一个新的i,count和interval。