Pregunta

Aquí está el escenario:

A mis usuarios se les presenta una cuadrícula, básicamente, una versión reducida de una hoja de cálculo. Hay cuadros de texto en cada fila de la cuadrícula. Cuando cambian un valor en un cuadro de texto, realizo una validación en su entrada, actualizo la colección que impulsa la cuadrícula y vuelvo a dibujar los subtotales en la página. Todo esto es manejado por el evento OnChange de cada cuadro de texto.

Cuando hacen clic en " Guardar " botón, estoy usando el evento OnClick del botón para realizar una validación final de las cantidades y luego enviar toda su entrada a un servicio web, guardándola.

Al menos, eso es lo que sucede si pasan el formulario hasta el botón Enviar.

El problema es que si ingresan un valor, inmediatamente hacen clic en el botón Guardar, SaveForm () comienza a ejecutarse antes de que se complete UserInputChanged (), una condición de carrera. Mi código no usa setTimeout, pero lo estoy usando para simular el lento código de validación 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 -->

No creo que pueda acelerar el código de validación: es bastante ligero, pero aparentemente lo suficientemente lento como para que el código intente llamar al servicio web antes de que se complete la validación.

En mi máquina, ~ 95 ms es el número mágico entre si el código de validación se ejecuta antes de que comience el código de guardado. Esto puede ser mayor o menor dependiendo de la velocidad de la computadora de los usuarios.

¿Alguien tiene alguna idea de cómo manejar esta condición? Un compañero de trabajo sugirió usar un semáforo mientras se ejecuta el código de validación y un bucle ocupado en el código de guardar para esperar hasta que se desbloquee el semáforo, pero me gustaría evitar usar cualquier tipo de bucle ocupado en mi código.

¿Fue útil?

Solución

Use el semáforo (llamémoslo StillNeedsValidating). si la función SaveForm ve que el semáforo StillNeedsValidating está activo, haga que active un segundo semáforo propio (que llamaré FormNeedsSaving aquí) y regrese. Cuando finaliza la función de validación, si el semáforo FormNeedsSaving está activo, llama a la función SaveForm por sí sola.

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

Otros consejos

Desactiva el botón Guardar durante la validación. Póngalo en deshabilitado como lo primero que hace la validación, y vuelva a habilitarlo cuando termine.

por ejemplo

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

y

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

Tendrá que ajustar cuando elimine setTimeout y haga que la validación sea una función, pero a menos que sus usuarios tengan reflejos sobrehumanos, debería estar listo.

Creo que el tiempo de espera está causando su problema ... si eso va a ser un código simple (sin llamadas asíncronas AJAX, tiempos de espera, etc.), entonces no creo que SaveForm se ejecute antes de que se complete UserInputChanged.

Un semáforo o mutex es probablemente la mejor manera de hacerlo, pero en lugar de un bucle ocupado, simplemente use un setTimeout () para simular un hilo dormido. Así:

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

Puede configurar una función recurrente que supervise el estado de toda la cuadrícula y genere un evento que indique si toda la cuadrícula es válida o no.

Su botón 'enviar formulario' se habilitaría o deshabilitaría según ese estado.

Oh, ahora veo una respuesta similar, eso también funciona, por supuesto.

Al trabajar con fuentes de datos asíncronas, ciertamente puede tener condiciones de carrera porque el hilo del proceso de JavaScript continúa ejecutando directivas que pueden depender de los datos que aún no han regresado de la fuente de datos remota. Es por eso que tenemos funciones de devolución de llamada.

En su ejemplo, la llamada al código de validación debe tener una función de devolución de llamada que pueda hacer algo cuando regrese la validación.

Sin embargo, al hacer algo con una lógica complicada o al intentar solucionar o mejorar una serie existente de devoluciones de llamada, puede volverse loco.

Esa es la razón por la que creé la biblioteca proto-q: http://code.google .com / p / proto-q /

Compruébalo si haces mucho de este tipo de trabajo.

No tiene una condición de carrera, las condiciones de carrera no pueden ocurrir en javascript, ya que javascript tiene un solo subproceso, por lo que 2 hilos no pueden interferir entre sí.

El ejemplo que da no es un muy buen ejemplo. La llamada setTimeout pondrá la función llamada en una cola en el motor de JavaScript y la ejecutará más tarde. Si en ese momento hace clic en el botón Guardar, la función setTimeout no se llamará hasta DESPUÉS de que el guardado haya finalizado por completo.

Lo que probablemente está sucediendo en su javascript es que el motor de javascript llama al evento onClick antes de que se llame al evento onChange.

Como pista, tenga en cuenta que javascript tiene un solo subproceso, a menos que use un depurador de javascript (firebug, microsoft screipt debugger). Esos programas interceptan el hilo y lo pausan. A partir de ese momento, se pueden ejecutar otros subprocesos (ya sea a través de eventos, llamadas setTimeout o controladores XMLHttp), haciendo que parezca que JavaScript puede ejecutar múltiples subprocesos al mismo tiempo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top