等到setInterval()完成

时间:2012-06-15 17:31:59

标签: javascript settimeout setinterval wait

我想在我的Javascript代码中添加一个小骰子滚动效果。我认为一个好的方法是使用setInterval()方法。我的想法是遵循代码(仅用于测试):

function roleDice() {
        var i = Math.floor((Math.random() * 25) + 5);   
        var j = i;
        var test = setInterval(function(){
            i--;
            document.getElementById("dice").src = "./images/dice/dice" + Math.floor((Math.random() * 6) + 1) + ".png";
            if(i < 1) {
                    clearInterval(test);
                }

            }, 50);     
    }

现在我想等待setInterval直到完成。所以我添加了一个setTimeout。

setTimeout(function(){alert("test")}, (j + 1) * 50);

这段代码工作得很好。 但是在我的主代码中,roleDice()函数返回一个值。现在我不知道如何处理......我无法从setTimeout()返回。如果我在函数末尾添加一个返回值,则返回将提升为快速。有没有人有想法,我怎么能解决这个问题?

修改 嗯,好吧,我明白回调量是多少,我想我知道它是如何工作的,但我还是有问题。我认为这更像是一个“界面”问题...... 这是我的代码:

function startAnimation(playername, callback) {
    var i = Math.floor((Math.random() * 25) + 5);
    var int = setInterval(function() {
        i--;
        var number = Math.floor((Math.random() * 6) + 1);
        document.getElementById("dice").src = "./images/dice/dice" + number + ".png";
        if(i < 1) {
            clearInterval(int);
            number = Math.floor((Math.random() * 6) + 1);
            addText(playername + " rolled " + number);
            document.getElementById("dice").src = "./images/dice/dice" + number + ".png";
            callback(number);
        }
    }, 50);
}

function rnd(playername) {
    var callback = function(value){
        return value; // I knew thats pointless...
    };
    startAnimation(playername, callback);
}

函数rnd()应该等待并返回值...我有点困惑。目前我不知道如何继续......代码等待var callback...,但我如何将它与回报结合起来呢?我想运行动画,然后将rnd()的最后一个数字返回到另一个函数。

5 个答案:

答案 0 :(得分:31)

当你接触到异步编程时,你偶然发现了大多数人在某些时候遇到的陷阱。

您不能“等待”超时/间隔完成 - 尝试这样做将无法工作或阻止整个页面/浏览器。任何应该在延迟之后运行的代码都需要从您在“完成”时传递给setInterval的回调中调用。

function rollDice(callback) {
    var i = Math.floor((Math.random() * 25) + 5);
    var j = i;
    var test = setInterval(function() {
        i--;
        var value = Math.floor((Math.random() * 6) + 1);
        document.getElementById("dice").src = "./images/dice/dice" + value + ".png";
        if(i < 1) {
            clearInterval(test);
            callback(value);
        }
    }, 50);
}

然后你就这样使用它:

rollDice(function(value) {
    // code that should run when the dice has been rolled
});

答案 1 :(得分:4)

更新TheifMaster答案:

您现在可以使用Promises

与回调类似,您可以使用Promises传递在程序运行完毕时调用的函数,如果使用reject,您也可以使用Promises处理错误。

function rollDice() {
  return new Promise((resolve, reject) => {
    const dice = document.getElementById("dice")

    let i = Math.floor((Math.random() * 25) + 5)

    const intervalId = setInterval(() => {
      const diceValue = Math.floor((Math.random() * 6) + 1)

      dice.src = `./images/dice/dice${diceValue}.png`

      if (--i < 1) {
        clearInterval(intervalId)
        resolve(diceValue)
      }
    }, 50)
  })
}

然后像这样使用它:

rollDice().then(value => alert(`Dice rolled: ${value}`))

答案 2 :(得分:2)

原则上你的代码都是顺序的。这是一个基本的骰子游戏,其中两个玩家滚动一个,他们看到谁有更大的数字。 [如果平局,第二人获胜!]

function roleDice() {
    return Math.floor(Math.random() * 6) + 1;
}

function game(){    
    var player1 = roleDice(),
        player2 = roleDice(),
        p1Win = player1 > player2;
    alert( "Player " + (p1Win ? "1":"2") + " wins!" );
}

game();

上面的代码非常简单,因为它只是流动。当你输入一个像滚动模具的异步方法时,你需要把东西拆成块来进行处理。

function roleDice(callback) {
    var i = Math.floor((Math.random() * 25) + 5);   
    var j = i;
    var test = setInterval(function(){
        i--;
        var die =  Math.floor((Math.random() * 6) + 1);
        document.getElementById("dice").src = "./images/dice/dice" + die + ".png";
        if(i < 1) {
                clearInterval(test);
                callback(die);  //Return the die value back to a function to process it
            }
        }, 50);
}

function game(){
    var gameInfo = {  //defaults
                       "p1" : null,
                       "p2" : null
                   },
        playerRolls = function (playerNumber) { //Start off the rolling
            var callbackFnc = function(value){ //Create a callback that will 
                playerFinishes(playerNumber, value); 
            };
            roleDice( callbackFnc );
        },
        playerFinishes = function (playerNumber, value) { //called via the callback that role dice fires
            gameInfo["p" + playerNumber] = value;
            if (gameInfo.p1 !== null && gameInfo.p2 !== null ) { //checks to see if both rolls were completed, if so finish game
                giveResult();
            }
        },
        giveResult = function(){ //called when both rolls are done
            var p1Win = gameInfo.p1 > gameInfo.p2;
            alert( "Player " + (p1Win ? "1":"2") + " wins!" );
        };            
    playerRolls("1");  //start player 1
    playerRolls("2");  //start player 2
}

game();

上面的代码可能更适合OOP类型的方式,但它可以工作。

答案 3 :(得分:1)

上述解决方案存在一些问题。运行程序不会(至少不在我的首选浏览器中)显示任何图像,因此必须在运行游戏之前加载这些图像。

另外,根据经验,我发现在预加载N个图像或让N个玩家掷骰子的情况下启动回调方法的最佳方法是让每个超时函数倒数到零,然后执行回调。这就像一个魅力,并不依赖于需要处理的项目数量。

<html><head><script>
var game = function(images){
   var nbPlayers = 2, winnerValue = -1, winnerPlayer = -1;
   var rollDice = function(player,callbackFinish){
      var playerDice = document.getElementById("dice"+player);
      var facesToShow = Math.floor((Math.random() * 25) + 5);   
      var intervalID = setInterval(function(){
         var face =  Math.floor(Math.random() * 6);
         playerDice.src = images[face].src;
         if (--facesToShow<=0) {
            clearInterval(intervalID);
            if (face>winnerValue){winnerValue=face;winnerPlayer=player}
            if (--nbPlayers<=0) finish();
         }
      }, 50);
   }
   var finish = function(){
      alert("Player "+winnerPlayer+" wins!");
   }      
   setTimeout(function(){rollDice(0)},10);
   setTimeout(function(){rollDice(1)},10);
}
var preloadImages = function(images,callback){
   var preloads = [], imagesToLoad = images.length;
   for (var i=0;i<images.length;++i){
      var img=new Image();
      preloads.push(img);
      img.onload=function(){if(--imagesToLoad<=0)callback(preloads)}
      img.src = images[i];
   }
}
preloadImages(["dice1.png","dice2.png","dice3.png","dice4.png","dice5.png","dice6.png"],game);
</script></head><body>
<img src="" id="dice0" /><img src="" id="dice1" /></body></html>

答案 4 :(得分:0)

要实现这个目标,使用普通的 setInterval 函数是根本不可能的。但是,还有更好的替代方法 setIntervalAsync。 setIntervalAsync 提供与 setInterval 相同的功能,但它保证该函数在任何给定时刻永远不会执行超过一次。

npm 我设置间隔异步

示例:

setIntervalAsync(
      () => {
        console.log('Hello')
        return doSomeWork().then(
          () => console.log('Bye')
        )
      },
      1000
    )