2008-12-03 10 views
14

Вот сценарий:Как избежать яваскрипта состояние гонки

Мои пользователи представлены сетки, в основном, урезанная версия таблицы. В каждой строке сетки есть текстовые поля. Когда они меняют значение в текстовом поле, я выполняю проверку на их входе, обновляя коллекцию, управляющую сеткой, и перерисовывая промежуточные итоги на странице. Все это обрабатывается событием 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 мс - это магическое число между тем, выполняется ли код проверки до начала кода сохранения. Это может быть выше или ниже в зависимости от скорости компьютера пользователя.

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

ответ

18

Использование семафора (назовем его 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; 
} 
+0

Точно, что мне нужно. Благодаря! – 2008-12-03 18:13:48

4

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

+0

Согласен. Возможно, лучшей симуляцией «медленного кода» является просто большой цикл «для», который ничего не делает. Таким образом, вы не освобождаете контроль над движком Javascript и должны следить за тем, чтобы события обрабатывались в правильном порядке. – 2008-12-03 18:14:03

+0

В фактическом коде нет setTimeouts. Существует серия скучных getElementByIds, isNan, parseInt и проверка, чтобы сумма всех текстовых полей не превышала предопределенную сумму - но ничего не было async. – 2008-12-03 18:23:19

7

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

например.

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 и сделать Validation одну функцию, но если пользователи не имеют сверхчеловеческие рефлексы, вы должны быть хорошо идти.

0

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

Кнопка «отправить форму» затем включит или отключит себя на основе этого состояния.

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

1

Семафор или мьютекс, вероятно, лучший способ пойти, но вместо цикла занятости просто используйте 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; 
} 
1

У вас нет условия гонки, условия гонки не может произойти в JavaScript Так как JavaScript однопоточный, так что 2 потоков не могут быть мешая друг другу.

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

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

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

0

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

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

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

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

Проверьте это, если вы делаете много такого рода работы.