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



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Используйте семафор (назовем его 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;
}
Я думаю, что тайм-аут вызывает вашу проблему ... если это будет простой код (без асинхронных вызовов AJAX, тайм-аутов и т.д.), то я не думаю, что SaveForm будет выполнен до завершения UserInputChanged.
В фактическом коде нет setTimeouts. Есть серия скучных getElementByIds, isNan, a parseInt и проверка, чтобы убедиться, что сумма всех текстовых полей не превышает заранее определенного количества - но ничего асинхронного.
Отключите кнопку сохранения во время проверки. Отключите его, как первое, что делает проверка, и снова включите по завершении.
например
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 и сделаете проверку одной функцией, но, если у ваших пользователей нет сверхчеловеческих рефлексов, все должно быть в порядке.
Вы можете настроить повторяющуюся функцию, которая отслеживает состояние всей сетки и вызывает событие, которое указывает, действительна вся сетка или нет.
Затем ваша кнопка «отправить форму» включается или отключается в зависимости от этого статуса.
О, теперь я вижу аналогичный ответ - это, конечно, тоже работает.
Семафор или мьютекс, вероятно, лучший способ пойти, но вместо цикла занятости просто используйте 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, поскольку javascript является однопоточным, поэтому 2 потока не могут мешать друг другу.
Приведенный вами пример - не очень хороший пример. Вызов setTimeout помещает вызываемую функцию в очередь в движке javascript и запускает ее позже. Если в этот момент вы нажмете кнопку сохранения, функция setTimeout не будет вызываться до ПОСЛЕ полного завершения сохранения.
Вероятно, в вашем javascript происходит то, что событие onClick вызывается механизмом javascript до вызова события onChange.
В качестве подсказки имейте в виду, что javascript является однопоточным, если вы не используете отладчик javascript (firebug, отладчик Microsoft Screipt). Эти программы перехватывают поток и приостанавливают его. С этого момента могут выполняться другие потоки (либо через события, вызовы setTimeout или обработчики XMLHttp), что создает впечатление, что javascript может запускать несколько потоков одновременно.
Конечно, вы можете иметь условия гонки в Javascript, это очень асинхронно! Есть только один поток пользовательского интерфейса, но любой тег <script>, загружающий javascript или любой XMLHTTPRequest, может вызвать состояние гонки. Если у вас есть 2 невыполненных async, вы не знаете порядок, в котором они будут завершаться, и ваш код должен это учитывать. Это 100% состояние гонки.
Он прав, у вас не может быть состояния гонки в одном потоке без ввода-вывода. Здесь нет состояния гонки с setTimeout. JS является асинхронным, но основан на цикле событий, который является однопоточным, поэтому цикл событий не будет вызываться, пока стек не станет пустым. Таким образом, поведение программы полностью определено, вам просто нужно правильно прочитать рабочий процесс кода, и вы будете знать, когда будет вызван цикл событий. В отличие от состояния гонки, когда оно действительно не определено, потому что у вас есть несколько параллельных потоков.
Неправильно. У вас могут быть условия гонки, вызванные разным порядком событий. Обычно это происходит с потоками, скажем, на Java, но условия гонки по своей сути связаны с неопределенными порядками событий / состояний и с тем, как их обрабатывать соответствующим образом. То, что он описывает, как раз и есть состояние гонки, и правильным решением, как указано выше, является использование шаблона проектирования стиля семафоров.
Кто-то проголосовал против меня. Итак, давайте удвоим это. Если вы думаете, что это не состояние гонки, потому что оно не связано с потоками, ВЫ НЕПРАВИЛЬНЫ. Период. en.wikipedia.org/wiki/Race_condition Вам даже не нужно, чтобы это было программное обеспечение, это может случиться с электрическими схемами. Это особенно распространено в многопоточном коде, но отнюдь не ТРЕБУЕТСЯ нескольких потоков.
Нет, в JavaScript нет условий гонки. Изучение (несуществующих) условий гонки в JavaScript и почему они никогда не могут произойти: blog.raananweber.com/2015/06/17/…
@Rampant Он прав. Условия гонки могут существовать Только, если у вас несколько потоков, чего нет в Javascript. Вы путаете состояние гонки с неизвестным порядком выполнения обратного вызова. Это такое же состояние гонки, как предоставление двух кнопок с двумя обратными вызовами щелчка и незнание, на какую из них пользователь нажмет первым. Другими словами, это не так. Пожалуйста, прочтите еще раз статью в Википедии, которую вы связали, чтобы полностью понять приведенный пример: en.wikipedia.org/wiki/Race_condition#Example
Нет. Вы не правы, и он неправ. См. Ту же статью и обсуждение условий гонки в электронных схемах. Точная ситуация, когда существует нажатие кнопки, а потоки - это абстракция, которой даже не существует.
В сообществе JS хорошо известно, что существуют условия гонки. Это просто не обычная многопоточная проблема. При определенных условиях (о которых разработчик может не знать) один и тот же код может быть исправлен немедленно или в ближайшем будущем. Наличие определенного порядка, привязанного к выполнению вашего кода, в этих случаях приведет к неожиданным результатам. Состояние гонки.
При работе с асинхронными источниками данных вы, безусловно, можете иметь условия гонки, потому что поток процесса JavaScript продолжает выполнять директивы, которые могут зависеть от данных, которые еще не были возвращены из удаленного источника данных. Вот почему у нас есть функции обратного вызова.
В вашем примере вызов кода проверки должен иметь функцию обратного вызова, которая может что-то делать при возврате проверки.
Однако, когда вы делаете что-то со сложной логикой или пытаетесь устранить или улучшить существующую серию обратных вызовов, вы можете сходить с ума.
Вот почему я создал библиотеку proto-q: http://code.google.com/p/proto-q/
Проверьте это, если вы много занимаетесь этим типом работы.
Я согласен. Возможно, лучшая симуляция «медленного кода» - это просто иметь большой цикл «for», который ничего не делает. Таким образом, вы не освобождаете управление движком Javascript и должны гарантировать, что события обрабатываются в правильном порядке.