JavaScript приостанавливает выполнение функции для ожидания пользовательского ввода

#javascript #pausing-execution

#javascript #приостановка выполнения

Вопрос:

Я пытаюсь создать что-то вроде игры, используя HTML5 canvas, JavaScript и XML. Идея в том, что вы можете создать тест, поместив вопросы и ответы в XML-файл. Я написал основной цикл, который перебирает все вопросы, задает их и проверяет правильность ответа. Сейчас я просто использую оповещения и диалоговые окна для ответа на вопросы Проблема в том, что мой основной цикл — это одно большое взаимосвязанное целое, которое проходит через всю игру от начала до конца, и вместо того, чтобы иметь окна оповещений, задающие вопросы, и диалоговые окна для ответа, сразу друг за другом, я хочу некоторого взаимодействия с пользователем. Ответы на вопросы появляются в окошках внизу экрана, и пользователь получает возможность управлять краном, чтобы выбрать правильный ответ. Вот фрагмент кода из основного цикла, на котором я застрял:

 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

            }
  

Как вы можете видеть, я вызываю loadQuestion, который мгновенно загружает вопрос, показывает возможные ответы и на данный момент немедленно выдает диалоговое окно, в котором вы можете ввести ответ.
Этот ответ возвращен и проверен.

Я уже запрограммировал элементы управления краном, пользователь уже может поднять коробку с его помощью. Но поскольку я вызываю loadQuestion и ожидаю, что он вернет значение, это не работает. Как мне заставить мой основной цикл «приостановиться» до тех пор, пока игрок, использующий кран, не даст ответ, а затем продолжить? Я уже пытался сделать ответ глобальной переменной и просто использовать пустое значение while answer == «», чтобы функция была занята, ничего не делая, пока answer не получит значение, но это просто замораживает скрипт. Я также возился с интервалами, пытаясь отслеживать состояние переменной ответа, а также очищать интервал и возвращать значение, когда это происходит, но это просто возвращает false, поскольку функция завершается без немедленного возврата значения.

Ответ №1:

Как мне заставить мой основной цикл «приостановиться» до тех пор, пока игрок, использующий кран, не даст ответ, а затем продолжить?

Разбивая ее. Единственный «выход» в JavaScript в браузерах — позволить вашей функции завершиться, а затем организовать повторный вызов позже (через setTimeout , setInterval обратный вызов ajax и т.д.). В вашем случае я склонен думать, что триггером для обратного вызова должно быть действие пользователя, отвечающее на предыдущий вопрос, например, click обработчик в полях ответов или что-то подобное (а не setTimeout и тому подобное, которые автоматизированы).

Например, этот код:

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

…может быть переделан следующим образом:

 function loopArrayAsync(ar, callback) {
    var index;

    index = 0;
    loop();

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

Вторая версия возвращает управление браузеру на каждой итерации цикла. Также важно отметить, что вторая версия возвращает данные до завершения циклов, в то время как первая версия ожидает завершения всех циклов, вот почему вторая версия имеет callback функцию, которую она вызывает по завершении цикла.

Код, вызывающий первую функцию, может выглядеть следующим образом:

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

…принимая во внимание, что использование асинхронной версии выглядит следующим образом:

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

Делая это, вы, вероятно, обнаружите, что используете замыкания ( loop выше приведено замыкание), и поэтому эта статья может быть полезной: Замыкания не сложны

Комментарии:

1. @Diodeus: О, вы знаете, что говорят: «Прошлая производительность не гарантирует будущих результатов»…

2. Спасибо за ответ, я все еще начинающий JavaScripter и никогда раньше не использовал функции обратного вызова. Спасибо за четкое объяснение и пример кода 🙂 Вы получаете принятый ответ, поскольку это ответило на мой первоначальный вопрос. doc_180 тоже был прав, хотя, я думаю, мне лучше вернуться к чертежной таблице для некоторого переосмысления.

3. @Jort: Рад, что помогло. И да, я бы также посмотрел на более крупную структуру. 🙂

Ответ №2:

Синхронизация отсутствует. приостановлены инструменты ввода / вывода в браузере JS, за исключением alert , confirm и prompt . Поскольку вы не хотите их использовать — попробуйте следующий шаблон:

 loadQuestion(i, function(answer) {
   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

        }
  resumeGameExecution();
});
  

Т.е. когда пользователь отвечает на него — выполняется функция обратного вызова, и вы знаете, что все готово.

Комментарии:

1. Спасибо за четкое объяснение, я бы проголосовал за полезность вашего ответа, если бы у меня была репутация, позволяющая это сделать 🙂

Ответ №3:

Насколько я понимаю, у вас есть приложение для опроса, в котором вы задаете ряд вопросов, а затем просите пользователя сбросить свой ответ, используя какой-либо элемент управления перетаскиванием (кран).

Я собираюсь перейти к касательной и сказать, что дизайн кажется неправильным. Javascript основан на событиях, и наличие одного основного потока, выполняющего цикл взаимодействия с пользователем, приведет к снижению удобства использования. Я не буду использовать какой-либо способ остановки потока (он же в Java). Javascript написан не для такого случая использования. Некоторые браузеры и почти все анализаторы производительности (например, Yslow) будут воспринимать ваше приложение как не отвечающее на запросы.

Поэтому я бы предоставил каждому вопросу div, идентифицируемый классом, который внутренне является последовательностью (вопрос1..2). Изначально будет включен или виден только один вопрос. Как только пользователь ответит на вопрос, я включу разрешенный вопрос. (при соответствующем событии, в данном случае, вероятно, при перетаскивании). Поскольку это последовательный процесс, мне просто нужно проверить, ответил ли пользователь на вопрос1, затем я соответствующим образом включу вопрос2. Весь поток должен управляться событиями. Событием здесь является пользователь, отвечающий на вопрос.

Комментарии:

1. В этом вы определенно правы. Просматривая мой код, первоначальный одиночный цикл был хорош для начального тестирования, но я думаю, что мне лучше просто выбросить его и сделать более управляемым событиями, как вы сказали.