JavaScript pausing execution of function to wait for user input

Jort picture Jort · Apr 5, 2011 · Viewed 60.7k times · Source

I'm trying to make a sort of game using the HTML5 canvas, JavaScript and XML. The idea is that you can make a quiz by putting questions and answers in an XML file. I wrote a main loop that loops through all the questions, poses them and checks the correctness of the answer. For now I'm simply using alerts and dialog boxes to answer the questions The problem is that my main loop is one big interconnected whole that walks through the entire game from beginning to end, and instead of having alert boxes posing questions and dialog boxes to answer, immediately after one another, I want some user interaction. The answers to the questions appear on boxes at the bottom of the screen, and the user gets to control a crane to pick the right answer. Here's the snippet of code from the main loop I'm stuck on:

answer = loadQuestion(i);
            if (answer == "correct") {
                // answered correctly, update scoreArray and load next question
                scoreArray[currentQuestion] = "correct";
                // show 'next'-button and wait for press to continue

            } else {
                // answered incorrectly again, update scoreArray and load next question
                scoreArray[currentQuestion] = "error";
                // show 'next'-button and wait for press to continue

            }

As you can see I'm calling loadQuestion, which instantly loads the question, shows the possible answers, and for now immediately throws a dialog box where you can type the answer. This answer is returned and validated.

I have already programmed the controls of the crane, the user can already pick up a box with it. But because I'm calling loadQuestion and expecting it to return a value, this doesn't work. How do I make my main loop "pause" until an answer has been given by the player using the crane, and then proceed? I already tried making the answer a global variable, and just having an empty while answer == "" to keep the function busy doing nothing until answer gets a value, but this just freezes the script. I also messed around with intervals in an attempt to monitor the status of the answer variable, and clear the interval and return the value when this happens, but that simply returns false since the function completes without immediately returning a value.

Answer

T.J. Crowder picture T.J. Crowder · Apr 5, 2011

How do I make my main loop "pause" until an answer has been given by the player using the crane, and then proceed?

By breaking it up. The only "yield" in JavaScript on browsers is to let your function end and then arrange to get called back later (via setTimeout, setInterval, an ajax callback, etc.). In your case, I'd tend to think the trigger to call you back should be the user's action answering the previous question, e.g., a click handler on the answer boxes or some such (rather than setTimeout and such, which are automated).

For instance, this code:

function loopArray(ar) {
    var index;
    for (index = 0; index < ar.length; ++index) {
       doSomething(ar[index]);
    }
}

...can be recast like this:

function loopArrayAsync(ar, callback) {
    var index;

    index = 0;
    loop();

    function loop() {
        if (index < ar.length) {
            doSomething(ar[index++]);
            setTimeout(loop, 0);
        }
        else {
            callback();
        }
    }
}

The second version yields control back to the browser on every loop iteration. It's also important to note that the second version returns before the loops have been completed, whereas the first version waits until all loops are done, which is why the second version has the callback function it calls when it's done looping.

Code calling the first one might look like this:

var a = ["one", "two", "three"];
loopArray(a);
// Code that expects the loop to be complete:
doTheNextThing();
doYetAnotherThing();

...whereas using the async version looks like this:

var a = ["one", "two", "three"];
loopArrayAsync(a, function() {
    // Code that expects the loop to be complete:
    doTheNextThing();
    doYetAnotherThing();
});

Doing this, you'll probably find you use closures (loop, above, is a closure), and so this article may be useful: Closures are not complicated