Wait until setInterval() is done
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".
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
Like callbacks, you can use Promises to pass a function that is called when the program is done running. If you use reject
you can also handle errors with Promises.
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
.
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 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.