Javascript 等到 setInterval() 完成

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11055530/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 04:15:20  来源:igfitidea点击:

Wait until setInterval() is done

javascriptsettimeoutsetintervalwait

提问by Andre Hofmeister

I would like to add a small dice rolling effect to my Javascript code. I think a good ways is to use the setInterval()method. My idea was following code (just for testing):

我想在我的 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);
}

Now I would like to wait for the setInterval until it is done. So I added a setTimeout.

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

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

This code works quite okay. But in my main code the roleDice()function returns a value. Now I don't know how I could handle that... I can't return from the setTimeout(). If I add a return to the end of the function, the return will raised to fast. Does anyone have a idea, how I could fix that?

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

EditHmm, okay I understand what the callback dose and I think I know how it works but I have still the problem. I think it's more a "interface" problem... Here my code:

编辑嗯,好吧,我明白回调剂量是多少,我想我知道它是如何工作的,但我仍然有问题。我认为这更像是一个“接口”问题......这是我的代码:

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

The function rnd()should wait and return the value… I'm a little bit confused. At the moment I have no clue how to going on... The code wait for the var callback...but how I could combined it with the return? I would like to run the animation and return after that the last number with rnd()to a other function.

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

回答by ThiefMaster

You stumbled into the pitfall most people hit at some point when they get in touch with asynchronous programming.

您偶然发现了大多数人在接触异步编程时会遇到的陷阱。

You cannot "wait" for an timeout/interval to finish - trying to do so would not work or block the whole page/browser. Any code that should run after the delay needs to be called from the callback you passed to setIntervalwhen it's "done".

您不能“等待”超时/间隔完成 - 尝试这样做将不起作用或阻止整个页面/浏览器。任何应该在延迟之后运行的代码都需要从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);
}

You then use it like this:

然后像这样使用它:

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

回答by ChrisBrownie55

UPDATE on TheifMaster's Answer:

更新 TheifMaster 的回答:

You can now use Promises

您现在可以使用承诺

Like callbacks you can use Promises to pass a function that is called when the program is done running, if you use rejectyou can also handle errors with 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)
  })
}

Then use it like this:

然后像这样使用它:

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

回答by epascarello

Orginally your code was all sequential. Here is a basic dice game where two players roll one and they see who has a bigger number. [If a tie, second person wins!]

最初你的代码都是顺序的。这是一个基本的骰子游戏,两个玩家掷一个,然后他们看看谁的数字更大。[如果平手,第二人获胜!]

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

The code above is really simple since it just flows. When you put in a asynchronous method like that rolling the die, you need to break up things into chunks to do processing.

上面的代码非常简单,因为它只是流动。当您使用像掷骰子这样的异步方法时,您需要将事物分解成块来进行处理。

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

The above code could be better in more of an OOP type of way, but it works.

上面的代码在更多的 OOP 类型的方式中可能会更好,但它有效。

回答by Christian Hammer

There are a few issues for the above solutions to work. Running the program doesn't (at least not in my preferred browser) show any images, so these has to be loaded before running the game.

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

Also, by experience I find the best way to initiate the callback method in cases like preloading N images or having N players throw a dice is to let each timeout function do a countdown to zero and at that point execute the callback. This works like a charm and does not rely on how many items needing to be processed.

此外,根据经验,我发现在预加载 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>