简体   繁体   English

等到 setInterval() 完成

[英]Wait until setInterval() is done

I would like to add a small dice rolling effect to my Javascript code.我想在我的 Javascript 代码中添加一个小骰子滚动效果。 I think a good ways is to use the setInterval() method.我认为一个好方法是使用setInterval()方法。 My idea was following code (just for testing):我的想法是遵循代码(仅用于测试):

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.现在我想等待 setInterval 直到它完成。 So I added a setTimeout.所以我添加了一个 setTimeout。

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

This code works quite okay.这段代码工作得很好。 But in my main code the roleDice() function returns a value.但在我的主代码中, roleDice() function 返回一个值。 Now I don't know how I could handle that... I can't return from the setTimeout() .现在我不知道我该如何处理......我无法从setTimeout()返回。 If I add a return to the end of the function, the return will raised to fast.如果我在 function 的末尾添加一个返回,返回将提高到快。 Does anyone have a idea, how I could fix that?有谁知道,我该如何解决?

Edit Hmm, 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. function rnd()应该等待并返回值……我有点困惑。 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?目前我不知道如何继续......代码等待var callback...但我如何将它与返回结合起来? I would like to run the animation and return after that the last number with rnd() to a other function.我想运行 animation,然后用rnd()将最后一个数字返回到另一个 function。

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

You can now use Promises and async/await您现在可以使用 Promises 和async/await

Like callbacks, you can use Promises to pass a function that is called when the program is done running.与回调类似,您可以使用 Promise 传递一个在程序运行完成时调用的函数。 If you use reject you can also handle errors with Promises.如果您使用reject您还可以使用承诺处理错误。

function rollDice() {
  return new Promise((resolve, reject) => {
    const dice = document.getElementById('dice');
    let numberOfRollsLeft = Math.floor(Math.random() * 25 + 5);

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

      // Display the dice's face for the new value
      dice.src = `./images/dice/dice${diceValue}.png`;

      // If we're done, stop rolling and return the dice's value
      if (--numberOfRollsLeft < 1) {
        clearInterval(intervalId);
        resolve(diceValue);
      }
    }, 50);
  });
}

Then, you can use the .then() method to run a callback when the promise resolves with your diceValue .然后,您可以使用.then()方法在使用您的diceValue解决承诺时运行回调。

rollDice().then((diceValue) => {
  // display the dice's value to the user via the DOM
})

Or, if you're in an async function, you can use the await keyword.或者,如果您在async函数中,则可以使用await关键字。

async function takeTurn() {
  // ...
  const diceValue = await rollDice()
  // ...
}

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 类型的方式中可能会更好,但它有效。

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.此外,根据经验,我发现在预加载 N 个图像或让 N 个玩家掷骰子等情况下启动回调方法的最佳方法是让每个超时函数倒计时为零,然后执行回调。 This works like a charm and does not rely on how many items needing to be processed.这就像一个魅力,不依赖于需要处理的项目数量。

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

To achieve that goal, using vanilla setInterval function is simply impossible.为了实现这个目标,使用普通的 setInterval 函数是根本不可能的。 However, there is better alternative to it, setIntervalAsync.但是,还有更好的替代方法 setIntervalAsync。 setIntervalAsync offers the same functionality as setInterval, but it guarantees that the function will never executed more than once at any given moment. setIntervalAsync提供与setInterval相同的功能,但它保证函数在任何给定时刻永远不会执行超过一次。

npm i set-interval-async npm 我设置间隔异步

Example:例子:

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

Example with Promises & setIntervals.. this is how I created a 'flat' chain of functions that wait until the other is completed...带有 Promises 和 setIntervals 的示例。这就是我创建“平面”函数链的方式,这些函数等待另一个函数完成……

The below snippet uses a library called RobotJS (here it returns a color at a specific pixel on screen) to wait for a button to change color with setInterval, after which it resolves and allows the code in the main loop to continue running.下面的代码片段使用一个名为 RobotJS 的库(这里它返回屏幕上特定像素的颜色)来等待按钮使用 setInterval 更改颜色,之后它会解析并允许主循环中的代码继续运行。

So we have a mainChain async function, in which we run functions that we declare below.所以我们有一个 mainChain 异步 function,我们在其中运行我们在下面声明的函数。 This makes it clean to scale, by just putting all your await someFunctions();只需将所有的await someFunctions(); one after each other:一个接一个:

async function mainChain() {
  await waitForColorChange('91baf1', 1400, 923)

  console.log('this is run after the above finishes')
}




async function waitForColorChange(colorBeforeChange, pixelColorX, pixelColorY) {
  return new Promise((resolve, reject) => {
    let myInterval = setInterval(() => {
      let colorOfNextBtn = robot.getPixelColor(pixelColorX, pixelColorY)

      if (colorOfNextBtn == colorBeforeChange) {
        console.log('waiting for color change')
      } else {
        console.log('color has changed')
        clearInterval(myInterval);
        resolve();
      }
    }, 1000)
  })
}


//Start the main function
mainChain()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM