1

I'm making a simple dice game, and I have set up a function that will randomly generate numbers and display them very quickly to simulate a sort of die being rolled. The problem I'm having is that I want to wait until the simulation is complete before I return the value, but instead the value is returned right away. Can anyone point me in the right direction? Essentially I want rollDie() to wait for diceSimulation() to be complete before continuing.

function rollDie(){

     diceSimulation();
     var result = Math.floor(Math.random() * 6) + 1;
     document.getElementById("result").innerHTML = result;
     return result;

}

function diceSimulation(){
     for (var i = 0; i < 30; i++) {
         var random; 
         (function (i) {
             setTimeout(function () {
                 random = Math.floor(Math.random() * 6) + 1;
                 document.getElementById("result").innerHTML = random;
             }, 50*i);
         })(i);
     }
}
ckorvee
  • 85
  • 1
  • 4
  • 15

2 Answers2

4

One option would be for diceSimulation to return a Promise that resolves after 50 * 31 ms, and for rollDie to await it:

async function rollDie(){

  await diceSimulation();
  var result = Math.floor(Math.random() * 6) + 1;
  document.getElementById("result").innerHTML = result;
  return result;

}

function diceSimulation(){
  return new Promise((resolve) => {
    for (var i = 0; i < 30; i++) {
      (function (i) {
        setTimeout(function () {
          const random = Math.floor(Math.random() * 6) + 1;
          document.getElementById("result").innerHTML = random;
        }, 50*i);
      })(i);
    }
    setTimeout(resolve, 1530);
  });
}

Note that rather than (function (i) { inside the loop, you might consider just using let instead of var, it's far nicer to read and debug. (Using var has too many problems - best to use const or let instead, whenever possible.) It would also be more elegant to select result once inside diceSimulation, rather than on every iteration of the loop, and unless you're deliberately inserting HTML markup, better to assign to testContent rather than innerHTML:

const result = document.getElementById("result");
for (let i = 0; i < 30; i++) {
  setTimeout(() => {
    const random = Math.floor(Math.random() * 6) + 1;
    result.textContent = random;
  }, 50*i);
}

Live demo:

async function rollDie() {

  await diceSimulation();
  var result = Math.floor(Math.random() * 6) + 1;
  document.getElementById("result").innerHTML = result;
  return result;

}

function diceSimulation() {
  return new Promise((resolve) => {
    const result = document.getElementById("result");
    for (let i = 0; i < 30; i++) {
      setTimeout(() => {
        const random = Math.floor(Math.random() * 6) + 1;
        result.textContent = random;
      }, 50 * i);
    }
    setTimeout(resolve, 1530);
  });
}

rollDie();
<div id="result"></div>
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • When I run this, the simulation still runs after I have displayed the result from rollDie(). It's almost like it waits till right after the result has been displayed to do the simulation – ckorvee Nov 28 '18 at 05:54
  • If you change the `i < 30` number, or the `}, 50 * i` number, you'll have to change the `1530` number appropriately - otherwise, `resolve` *should* be called after all timeouts in the `for` loop have finished. Actually, since the final action is the same as the loop action, why not leave out the final action entirely, and iterate from 0 to 30 instead of from 0 to 29? – CertainPerformance Nov 28 '18 at 05:56
  • I didn't change anything, just pasted it in and it was doing that. I see the result displayed and then I see the simulation. It looks like rollDie() is actually returning [object Promise] instead of a number as well. – ckorvee Nov 28 '18 at 06:04
  • In order to consume `Promises` you have to call `.then` on them (or `await` them, as in the answer). See edit, seems to work just fine to me – CertainPerformance Nov 28 '18 at 06:06
  • Hmmm I'm still having the problem. Does it matter that I'm calling rollDie() from another function? Do I have to do another await? – ckorvee Nov 28 '18 at 06:12
  • Like I said, in order to get the value from `Promises`, you must either call `.then` on the `Promise` or `await` it. See https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call Because `rollDie` is asynchronous, if you want a call of the function to resolve to a value, you'll have to use one of the above methods – CertainPerformance Nov 28 '18 at 06:15
  • I think I have it figured out, I made my other function await rollDie() and its close to working now. Thank you! – ckorvee Nov 28 '18 at 06:16
1

You can achieve your expected result using a callback or a promise . A simple example below shows how you can use promises as a solution :

let diceSimulation = () => {
console.log("Dice simulation complete");
};
let rollDie = new Promise((resolve,reject)=> {
resolve("Roll die is completed");
})

rollDie.then((message)=> {
    console.log(message);

    diceSimulation();
},
(fail)=> {

})
Terrorbladezz
  • 81
  • 1
  • 5