web-dev-qa-db-fra.com

Attendez que setInterval () soit terminé

J'aimerais ajouter un petit effet de dé à mon code Javascript. Je pense qu'un bon moyen est d'utiliser la méthode setInterval(). Mon idée était de suivre le code (juste pour tester):

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

Maintenant, j'aimerais attendre que setInterval soit terminé. J'ai donc ajouté un setTimeout.

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

Ce code fonctionne assez bien. Mais dans mon code principal, la fonction roleDice() renvoie une valeur. Maintenant, je ne sais pas comment je pourrais gérer ça ... Je ne peux pas revenir de la setTimeout(). Si j'ajoute un retour à la fin de la fonction, le retour sera élevé trop vite. Quelqu'un at-il une idée, comment je pourrais résoudre ce problème?

Edit Hmm, ok je comprends ce que la dose de rappel et je pense que je sais comment ça marche mais j'ai toujours le problème. Je pense que c’est plus un problème "d’interface" ... Voici mon 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);
}

La fonction rnd() devrait attendre et renvoyer la valeur… Je suis un peu confus. Pour le moment, je ne sais pas comment continuer. Le code attend le var callback..., mais comment puis-je le combiner avec le retour? Je voudrais lancer l'animation et retourner ensuite le dernier numéro avec rnd() à une autre fonction.

11
hofmeister

Vous êtes tombé dans le piège que la plupart des gens rencontrent à un moment donné lorsqu'ils entrent en contact avec la programmation asynchrone.

Vous ne pouvez pas "attendre" la fin d'un délai/d'un intervalle. Essayer de le faire ne fonctionnerait pas et ne bloquerait pas l'intégralité de la page/du navigateur. Tout code devant être exécuté après le délai doit être appelé à partir du rappel que vous avez transmis à setInterval quand il est "terminé".

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

Vous l'utilisez ensuite comme ceci:

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

UPDATE sur la réponse de TheifMaster:

Vous pouvez maintenant utiliser les promesses

Comme pour les rappels, vous pouvez utiliser Promises pour transmettre une fonction appelée lorsque le programme est exécuté. Si vous utilisez reject, vous pouvez également gérer les erreurs avec 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)
  })
}

Ensuite, utilisez-le comme ceci:

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

Il existe quelques problèmes pour que les solutions ci-dessus fonctionnent. L'exécution du programme (du moins pas dans mon navigateur préféré) n'affiche aucune image, elle doit donc être chargée avant l'exécution du jeu.

Aussi, par expérience, je trouve que le meilleur moyen d'initier la méthode de rappel dans des cas tels que le préchargement de N images ou le fait que N joueurs lancent un dé consiste à laisser chaque fonction de délai écoulé faire un compte à rebours jusqu'à zéro et exécuter le rappel à ce stade. Cela fonctionne comme un charme et ne dépend pas du nombre d’articles devant être traités.

<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>
1
Christian Hammer

D'origine, votre code était tout séquentiel. Voici un jeu de dés de base dans lequel deux joueurs lancent un et voient qui a le plus grand nombre. [En cas d'égalité, la deuxième personne gagne!]

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

Le code ci-dessus est vraiment simple puisqu'il coule juste. Lorsque vous utilisez une méthode asynchrone comme celle qui consiste à lancer le dé, vous devez diviser les éléments en morceaux pour effectuer le traitement.

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

Le code ci-dessus pourrait être meilleur dans plus d'un type de manière OOP, mais cela fonctionne.

1
epascarello