Вопрос

Вот такой сценарий:

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

Когда они нажимают кнопку "Сохранить", я использую событие OnClick кнопки для выполнения некоторой окончательной проверки сумм, а затем отправляю весь их ввод в веб-службу, сохраняя его.

По крайней мере, это то, что происходит, если они переходят через форму к кнопке "Отправить".

Проблема в том, что если они вводят значение, а затем сразу нажимают кнопку сохранить, SaveForm() начинает выполняться до завершения UserInputChanged() - условие гонки.Мой код не использует setTimeout, но я использую его для имитации вялого кода проверки UserInputChanged:

 <!-- snip -->
 <script>
    var amount = null;
    var currentControl = null;

    function UserInputChanged(control) {
        currentControl = control;
        // use setTimeout to simulate slow validation code (production code does not use setTimeout)
        setTimeout("ValidateAmount()", 100); 
    }

    function SaveForm() {
        // call web service to save value
        document.getElementById("SavedAmount").innerHTML = amount;
    }

    function ValidateAmount() {
        // various validationey functions here
        amount = currentControl.value; // save value to collection
        document.getElementById("Subtotal").innerHTML = amount; // update subtotals

    }
</script>
<!-- snip -->
Amount: <input type="text" id="UserInputValue" onchange="UserInputChanged(this);" /> <br />
Subtotal: <span id="Subtotal"></span> <br />
<input type="button" onclick="SaveForm();" value="Save" /> <br /><br />
Saved amount: <span id="SavedAmount"></span>
<!-- snip -->

Я не думаю, что смогу ускорить код проверки - он довольно легкий, но, по-видимому, достаточно медленный, чтобы код пытался вызвать веб-службу до завершения проверки.

На моей машине ~ 95 мс - это магическое число между выполнением кода проверки до начала сохранения кода.Это значение может быть выше или ниже в зависимости от скорости работы компьютера пользователя.

У кого-нибудь есть какие-нибудь идеи, как справиться с этим состоянием?Коллега предложил использовать семафор во время выполнения кода проверки и цикл занятости в коде сохранения, чтобы дождаться разблокировки семафора, но я бы хотел избежать использования какого-либо цикла занятости в моем коде.

Это было полезно?

Решение

Используйте семафор (давайте назовем его StillNeedsValidating).если функция SaveForm видит, что семафор StillNeedsValidating активирован, попросите ее активировать второй собственный семафор (который я буду называть здесь FormNeedsSaving) и вернуть.Когда функция проверки завершается, если семафор FormNeedsSaving включен, она вызывает функцию SaveForm самостоятельно.

В jankcode;

function UserInputChanged(control) {
    StillNeedsValidating = true;
    // do validation
    StillNeedsValidating = false;
    if (FormNeedsSaving) saveForm(); 
}

function SaveForm() {
    if (StillNeedsValidating) { FormNeedsSaving=true; return; }
    // call web service to save value
    FormNeedsSaving = false;
}

Другие советы

Отключите кнопку сохранить во время проверки.Установите для него значение отключено, как первое, что делает проверка, и повторно включите его по завершении.

например ,

function UserInputChanged(control) {
    // --> disable button here --< 
    currentControl = control;
    // use setTimeout to simulate slow validation code (production code does not use setTimeout)
    setTimeout("ValidateAmount()", 100); 
}

и

function ValidateAmount() {
    // various validationey functions here
    amount = currentControl.value; // save value to collection
    document.getElementById("Subtotal").innerHTML = amount; // update subtotals
    // --> enable button here if validation passes --<
}

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

Я думаю, что тайм-аут является причиной вашей проблемы...если это будет простой код (без асинхронных вызовов AJAX, тайм-аутов и т.д.), То я не думаю, что SaveForm будет выполнен до завершения UserInputChanged.

Семафор или мьютекс, вероятно, лучший способ, но вместо цикла занятости просто используйте setTimeout() чтобы имитировать спящий режим потока.Вот так:

busy = false;

function UserInputChanged(control) {
    busy = true;
    currentControl = control;
    // use setTimeout to simulate slow validation code (production code does not use setTimeout)
    setTimeout("ValidateAmount()", 100); 
}

function SaveForm() {
    if(busy) 
    {
       setTimeout("SaveForm()", 10);
       return;
    }

    // call web service to save value
    document.getElementById("SavedAmount").innerHTML = amount;
}

function ValidateAmount() {
    // various validationey functions here
    amount = currentControl.value; // save value to collection
    document.getElementById("Subtotal").innerHTML = amount; // update subtotals
    busy = false;
}

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

Затем ваша кнопка "отправить форму" будет сама включаться или отключаться в зависимости от этого статуса.

О, сейчас я вижу похожий ответ - это, конечно, тоже работает.

При работе с асинхронными источниками данных у вас, безусловно, могут возникнуть условия гонки, потому что поток процесса JavaScript продолжает выполнять директивы, которые могут зависеть от данных, которые еще не были возвращены из удаленного источника данных.Вот почему у нас есть функции обратного вызова.

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

Однако, создавая что-то со сложной логикой или пытаясь устранить неполадки или улучшить существующую серию обратных вызовов, вы можете сойти с ума.

Именно по этой причине я создал библиотеку proto-q: http://code.google.com/p/proto-q/

Проверьте это, если вы часто выполняете подобную работу.

У вас нет условия гонки, условия гонки не могут выполняться в javascript, поскольку javascript является однопоточным, поэтому 2 потока не могут мешать друг другу.

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

Что, вероятно, происходит в вашем javascript, так это то, что событие onClick вызывается движком javascript перед вызовом события onChange.

В качестве подсказки имейте в виду, что javascript является однопоточным, если только вы не используете отладчик javascript (firebug, Microsoft screipt debugger).Эти программы перехватывают поток и приостанавливают его.С этого момента могут запускаться другие потоки (либо с помощью событий, вызовов setTimeout, либо обработчиков XMLHttp), из-за чего создается впечатление, что javascript может запускать несколько потоков одновременно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top