Domanda

Ecco lo scenario:

Ai miei utenti viene presentata una griglia, in sostanza, una versione ridotta di un foglio di calcolo. Ci sono caselle di testo in ogni riga della griglia. Quando cambiano un valore in una casella di testo, eseguo la convalida del loro input, aggiornando la raccolta che guida la griglia e ridisegnando i totali parziali sulla pagina. Tutto questo è gestito dall'evento OnChange di ogni casella di testo.

Quando fanno clic su " Salva " pulsante, sto utilizzando l'evento OnClick del pulsante per eseguire una convalida finale sugli importi, quindi inviare l'intero input a un servizio Web, salvandolo.

Almeno, è quello che succede se passano attraverso il modulo al pulsante Invia.

Il problema è che, se inseriscono un valore, quindi fanno clic immediatamente sul pulsante Salva, SaveForm () inizia l'esecuzione prima che UserInputChanged () venga completato, una condizione di competizione. Il mio codice non usa setTimeout, ma lo sto usando per simulare il lento codice di validazione 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 -->

Non credo di poter accelerare il codice di convalida: è piuttosto leggero, ma apparentemente abbastanza lento da consentire al codice di chiamare il servizio Web prima che la convalida sia completa.

Sulla mia macchina, ~ 95ms è il numero magico tra l'esecuzione del codice di convalida prima dell'inizio del codice di salvataggio. Questo può essere superiore o inferiore a seconda della velocità del computer dell'utente.

Qualcuno ha qualche idea su come gestire questa condizione? Un collega ha suggerito di utilizzare un semaforo mentre il codice di convalida è in esecuzione e un ciclo occupato nel codice di salvataggio per attendere fino allo sblocco del semaforo, ma vorrei evitare di utilizzare qualsiasi tipo di ciclo occupato nel mio codice.

È stato utile?

Soluzione

Usa il semaforo (chiamiamolo StillNeedsValidating). se la funzione SaveForm rileva che il semaforo StillNeedsValidating è attivo, attivalo un secondo semaforo (che chiamerò FormNeedsSaving qui) e ritorna. Al termine della funzione di convalida, se il semaforo FormNeedsSaving è attivo, chiama la funzione SaveForm da sola.

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

Altri suggerimenti

Disabilita il pulsante di salvataggio durante la convalida. Impostalo su disabilitato come la prima cosa che fa la convalida e riattivalo al termine.

per es.

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

e

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

Dovrai regolare quando rimuovi setTimeout e rendi la convalida una funzione, ma a meno che i tuoi utenti non abbiano riflessi sovrumani, dovresti essere pronto per andare.

Penso che il timeout stia causando il tuo problema ... se si tratta di un codice semplice (nessuna chiamata AJAX asincrona, timeout ecc.), quindi non penso che SaveForm verrà eseguito prima che UserInputChanged venga completato.

Un semaforo o mutex è probabilmente il modo migliore per andare, ma invece di un ciclo occupato, basta usare un setTimeout () per simulare un thread sleep. In questo modo:

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

È possibile impostare una funzione ricorrente che monitora lo stato dell'intera griglia e genera un evento che indica se l'intera griglia è valida o meno.

Il pulsante "invia modulo" si abiliterà o disabiliterà se stesso in base a quello stato.

Oh, vedo una risposta simile ora - anche quella funziona, ovviamente.

Quando si lavora con origini dati asincrone è possibile che si verifichino condizioni di competizione perché il thread del processo JavaScript continua a eseguire direttive che potrebbero dipendere dai dati che non sono ancora tornati dall'origine dati remota. Ecco perché abbiamo funzioni di callback.

Nel tuo esempio, la chiamata al codice di validazione deve avere una funzione di callback che può fare qualcosa quando la validazione ritorna.

Tuttavia, quando si crea qualcosa con una logica complicata o si tenta di risolvere i problemi o migliorare una serie esistente di callback, si può impazzire.

Questo è il motivo per cui ho creato la libreria proto-q: http://code.google com / p / proto-q /

Dai un'occhiata se fai molto di questo tipo di lavoro.

Non hai una condizione di gara, le condizioni di gara non possono verificarsi in javascript poiché javascript è a thread singolo, quindi 2 thread non possono interferire tra loro.

L'esempio che dai non è un ottimo esempio. La chiamata setTimeout inserirà la funzione chiamata in una coda nel motore JavaScript e la eseguirà in seguito. Se a quel punto fai clic sul pulsante Salva, la funzione setTimeout non verrà chiamata finché DOPO il completamento del salvataggio.

Ciò che probabilmente sta accadendo nel tuo javascript è che l'evento onClick viene chiamato dal motore javascript prima che venga chiamato l'evento onChange.

Come suggerimento, tieni presente che javascript è a thread singolo, a meno che tu non usi un debugger javascript (firebug, microsoft screipt debugger). Quei programmi intercettano il thread e lo mettono in pausa. Da quel momento in poi possono essere eseguiti altri thread (tramite eventi, chiamate setTimeout o gestori XMLHttp), facendo sembrare che JavaScript possa eseguire più thread contemporaneamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top