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()
  // ...
}