Frage

Hier ist das Szenario:

Meine Nutzer sind ein Raster dargestellt, im Grunde eine abgespeckte Version einer Kalkulationstabelle. Es gibt Textfelder in jeder Zeile im Gitter. Wenn sie einen Wert in einem Textfeld zu ändern, ich bin die Durchführung Validierung auf ihrer Eingabe, die Sammlung zu aktualisieren, die das Netz fährt, und auf der Seite, um die Teilergebnisse neu erstellt. Dies alles wird behandelt durch das OnChange-Ereignis jedes Textfeld ein.

Wenn sie die Schaltfläche „Speichern“ klicken, ich bin mit dem OnClick-Ereignis der Schaltfläche eine endgültige Bestätigung über die Beträge führen und dann ihre gesamte Eingabe an einen Webdienst senden, es zu speichern.

Zumindest das ist, was passiert, wenn sie Registerkarte über das Formular auf die Schaltfläche Senden.

Das Problem ist, wenn sie einen Wert eingeben, dann sofort auf die Schaltfläche Speichern klicken, SaveForm () beginnt mit der Ausführung vor UserInputChanged () abgeschlossen ist - eine Race-Bedingung. Mein Code nicht verwendet SetTimeout nicht, aber ich bin mit dem träge UserInputChanged Validierungscode zu simulieren:

 <!-- 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 -->

Ich glaube nicht, dass ich den Validierungscode beschleunigen kann - es ist ziemlich leicht, aber es scheint, langsam genug, dass Code versucht, den Web-Service aufrufen, bevor die Validierung abgeschlossen ist

.

Auf meinem Rechner ~ 95ms ist die magische Zahl zwischen ob der Validierungscode wird ausgeführt, bevor die beginnt speichern Code. Dies kann höher oder niedriger sein in Abhängigkeit von der Geschwindigkeit des Computers des Benutzers.

Hat jemand irgendwelche Ideen, wie diese Bedingung zu behandeln? Ein Kollege schlug vor, eine Semaphore verwendet, während der Validierungscode ausgeführt wird und ein Besetzt Schleife im speichern Code zu warten, bis die Semaphore entriegelt - aber ich möchte in meinem Code verwenden, jede Art von beschäftigt Schleife vermeiden

.
War es hilfreich?

Lösung

Mit der Semaphore (nennen wir es StillNeedsValidating). wenn die SaveForm Funktion der StillNeedsValidating Semaphore sieht nach oben ist, haben sie eine zweite Semaphore seiner eigenen aktivieren (was ich FormNeedsSaving hier nennen werde) und zurück. Wenn die Validierungsfunktion beendet ist, wenn die FormNeedsSaving Semaphore abgelaufen ist, ruft es die SaveForm Funktion auf seinem eigenen.

In 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;
}

Andere Tipps

Deaktivieren Sie die Schaltfläche Speichern bei der Validierung. Setzen Sie ihn auf Behinderte als das erste, was die Validierung der Fall ist, und aktivieren Sie es, wie es endet.

z.

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

und

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 --<
}

Sie müssen einstellen, wenn Sie die setTimeout entfernen und die Validierung einer Funktion machen, aber es sei denn, Ihre Benutzer übermenschliche Reflexe haben, sollten Sie gut zu gehen.

ich glaube, das Timeout Ihr Problem verursacht ... wenn das wird schlicht Code sein (keine asynchrone AJAX-Aufrufe, Timeouts etc), dann glaube ich nicht, dass SaveForm ausgeführt wird, bevor UserInputChanged abgeschlossen ist.

Eine Semaphore oder Mutex ist wahrscheinlich der beste Weg zu gehen, aber statt einem langen Schleife, verwenden Sie einfach eine setTimeout() einen Thread Schlaf zu simulieren. Wie folgt aus:

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;
}

Sie können eine wiederkehrende Funktion einrichten, die den Zustand des gesamten Gitters überwacht und löst ein Ereignis, das das gesamte Netz gültig ist oder nicht angibt, ob.

Ihr ‚Formular senden‘ -Button würde dann aktivieren oder deaktivieren selbst auf der Grundlage dieses Status.

Oh, ich sehe eine ähnliche Antwort jetzt - die natürlich auch funktioniert

.

Wenn Sie mit Asynchron-Datenquellen arbeiten, können Sie sicherlich Rennbedingungen haben, weil die JavaScript Prozess-Thread-Richtlinien auszuführen weiterhin, die auf dem Datum abhängen, die von der fernen Datenquelle noch nicht zurückgekehrt ist. Deshalb haben wir Callback-Funktionen haben.

In Ihrem Beispiel der Aufruf an den Validierungscode muss eine Rückruffunktion haben, die etwas tun können, wenn die Überprüfung zurückkehrt.

Wenn jedoch etwas mit komplizierter Logik zu machen oder versuchen, eine bestehende Reihe von Rückrufen zu beheben oder zu verbessern, können Sie Nüsse geht.

Das ist der Grund, warum ich die Proto-q-Bibliothek erstellt: http://code.google .com / p / Proto-q /

Überprüfen Sie es heraus, wenn Sie viel von dieser Art von Arbeit zu tun.

Sie haben nicht eine Race-Bedingung haben, können Bedingungen Rennen nicht in JavaScript geschehen, da Javascript Single-Threaded ist so 2 Fäden nicht miteinander stören.

Das Beispiel, das Sie geben, ist nicht ein sehr gutes Beispiel. Der setTimeout Anruf wird die genannte Funktion in einer Warteschlange in dem JavaScript-Engine gesetzt, und es später laufen. Wenn an diesem Punkt Sie auf die Schaltfläche Speichern klicken, wird die setTimeout-Funktion erst nach dem Speichern aufgerufen werden vollständig abgeschlossen.

Was ist wohl in Ihrem Javascript passiert ist, dass das OnClick-Ereignis von der JavaScript-Engine aufgerufen wird, bevor das onChange Ereignis aufgerufen wird.

Als Hinweis, bedenken Sie, dass Javascript Single-Threaded ist, es sei denn, Sie einen JavaScript-Debugger verwenden (Firebug, microsoft screipt Debugger). Diese Programme abfangen den Faden und pausieren es. Von diesem Punkt an anderen Threads (entweder über Ereignisse, SetTimeout Anrufe oder XMLHttp Handler) kann dann ausgeführt werden, so dass es scheint, dass Javascript mehrere Threads gleichzeitig ausgeführt werden können.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top