2010-01-16 12 views
16

Tengo una aplicación web donde hay un temporizador que está constantemente en cuenta regresiva. Mientras tanto, el cliente comprueba frecuentemente con el servidor para ver si se ha agregado más tiempo al temporizador. El código es como la siguiente:concurrencia de Ajax

function tick() { 
    // This function is called once every second 
    time -= 1; 
    redisplay(time); 
}; 
function update(newtime) { 
    // This function is called whenever the ajax request 
    // to the server yields a new time 
    time = newtime; 
}; 

Es, por supuesto, un poco más complejo que eso, pero se puede ver la condición inherente a la raza. ¿Qué sucede si la actualización y la función de marcación están tratando de modificar time al mismo tiempo?

Francamente, no sé casi lo suficiente de JavaScript para entender cómo lidiar con este tipo de problema de concurrencia: ¿hay una manera fácil de hacerlo, o sino, puede alguien dirigirme hacia los recursos donde puedo aprender más ?

Gracias.

+0

Siempre quise hacer esta pregunta. – zedoo

+1

¡Muy buena pregunta! –

+1

Solo para mayor claridad, ¿se está ejecutando este código en un navegador, o en el servidor ejecutándose en algo como node.js? –

Respuesta

16

No tiene una condición de carrera, porque Javascript no se ejecuta al mismo tiempo.

Cada vez que se activa una devolución de llamada desde una operación asíncrona (AJAX, setTimeout, etc.), esa devolución de llamada debe finalizar la ejecución antes de que se pueda invocar otra devolución de llamada de otra operación asincrónica. Entonces, si se está ejecutando update(), no hay otro Javascript. Una vez que update() finaliza, pueden activarse otras devoluciones de llamadas desde operaciones asincrónicas (por ejemplo, tick()). Curiosamente, esta es la razón por la cual setTimeout y su tipo no están garantizados para ejecutarse en el momento preciso en que se alcanza el tiempo de espera: algunos otros javascript podrían estar bloqueando la ejecución de la devolución de llamada.

Consulte http://ejohn.org/blog/how-javascript-timers-work/ para obtener una buena información sobre cómo funciona esto.

-2

Mientras agregue algunos segundos más a su tiempo actual, debería estar bien.

En lugar de hacer time = newtime; intente time += newtime; De esta manera no perderá el segundo que le preocupa.

Aún así, solo está perdiendo un segundo en el peor.

+0

Nota, newtime es cuánto tiempo más agregar. –

+5

No creo que esto resuelva el problema de condición de carrera –

0

Me equivoqué: ¡esto no resuelve el problema! (Explicación después del código)

NEWTIME = false; 
function tick() { 
    // This function is called once every second 
    if (NEWTIME) { 
     time = NEWTIME; 
     NEWTIME = false; 
    } 
    time -= 1; 
    redisplay(time); 
}; 
function update(newtime) { 
    // This function is called whenever the ajax request 
    // to the server yields a new time 
    NEWTIME = newtime; 
}; 

El problema de esta solución es que mal haciendo de esta manera sólo hay que mover el tema de la condición de carrera variable de time a la variable NEWTIME.

Sólo piensa en esto: la ejecución de tick alcances y ejecuta la línea time = NEWTIME; ahora, antes de continuar, actualizar ser llamado y NEWTIME obtiene un valor X. Ahora la ejecución de tick continúa ejecutándose NEWTIME = false;. De esta forma, ha perdido el valor X de NEWTIME y, por lo tanto, el efecto de una llamada a la actualización()!

+0

Esto es exactamente lo que tenía la intención de hacer, ¿cómo es que esto no aborda la condición de carrera? Puede que esté fuera por un ciclo, pero no me parece que se perderá 'NEWTIME'. –

+0

@Justin De esta forma, solo mueve el problema de condición de carrera de la variable 'time' a la variable' NEWTIME'. Simplemente piense esto: la ejecución de tick alcanza y ejecuta la línea 'time = NEWTIME;' ahora, antes de continuar, 'update' se llama y' NEWTIME' obtiene un valor 'X'. Ahora la ejecución 'tick' continúa ejecutando' NEWTIME = false; '. ¡De esta manera ha perdido el efecto de una llamada 'update()'! –

+0

Sí, pero solo lo pierde por un máximo de un tic, en lugar de hacerlo de forma original, lo que hace que se pierda para siempre * si * el idioma era concurrente. –

-1

El problema necesita algunos semáforos. Hago mucho para enviar ajax después de que termina el anterior. Su caso es un poco parecido;)

Pruebe algo así. Debe ignorar todos los intentos de disminuir el tiempo que colisiona con la devolución de llamada ajax.

window.TimeKeeper = new function(){ 
this.time=0; //initial value, whatever 
this.isopen=true; 

this.decrement = function(){ 
if(this.isopen){ 
    this.time--; 
    redisplay(this.time); 
    } 
} 
this.set = function(val){ 
if(this.isopen){ 
    this.isopen=false; 
    this.time=val; 
    this.isopen=true; 
    } else { 
    //another AJAX callback is modifying time. 
    //You can enqueue current value and put it later 
    } 
} 

} 

function tick() { 
    TimeKeeper.decrement(); 

}; 
function update(newtime) { 
    TimeKeeper.set(newtime); 
}; 

Una cosa más - el setTimeout funciona como un nuevo hilo y yo esperaría que los navegadores hacen la sincronización de acceso mem, lo que podría ser suficiente para comprobar si el valor no creció antes de decremento. Pero la solución anterior es más flexible y segura.

Y un pequeño consejo - evitar la consulta con AJAX con demasiada frecuencia - que puede causar problemas adicionales - como las solicitudes que llegan en orden diferente que envió, y el uso de memoria de Firefox se acumulan una gran cantidad de exceso de ajax un minuto;)

+0

Gracias. ¿Qué sucede si la disminución falla, sin embargo? El tiempo no se vuelve a mostrar. En este caso, podría simplemente mover la llamada de presentación fuera del if, pero para un caso más general, ¿existe una forma de bloqueo sin bloqueo para volver a intentarlo en un momento? Además, necesito verificar con bastante frecuencia las actualizaciones del servidor, lo que en este momento significa que estoy haciendo una llamada ajax por segundo. Si necesito actualizaciones con frecuencia, ¿hay una mejor manera de hacerlo que ajax? No hay forma de que el servidor envíe un aviso a los clientes cuando ocurre un evento, ¿verdad? – So8res

+0

No tengo tiempo ahora para revisar profundamente, pero esta no parece una buena solución para mí ... –

+2

Javascript tiene un solo hilo. No necesitas semáforos. Lo peor que ocurre es que la disminución ocurre (seguida de una nueva visualización), y luego el valor se sobrescribe inmediatamente con un nuevo valor provisto por la actualización. En ese caso, el tiempo se reducirá y se mostrará un segundo después. No es un problema. – PanCrit

8

Javascript es de un solo hilo. No hay condiciones de carrera. No hay forma en javascript para que las dos líneas time = newtime; y time -= 1; se superpongan durante la ejecución. De hecho, las dos funciones están garantizadas para no superponerse. Uno de ellos se ejecutará y luego el otro se ejecutará.