2010-01-16 16 views
16

Possiedo un'applicazione Web in cui è presente un timer che esegue continuamente il conto alla rovescia. Nel frattempo, il client controlla spesso con il server per vedere se è stato aggiunto più tempo al timer. Il codice simile a questa:Ajax simultanea

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

E ', ovviamente, un po' più complesso di così, ma si può vedere la condizione di competizione inerente. Cosa succede se l'aggiornamento e la funzione di spunta stanno entrambi cercando di modificare time allo stesso tempo?

Francamente, non conosco abbastanza javascript per capire come affrontare questo tipo di problema di concorrenza: c'è un modo semplice per farlo, o se no, qualcuno può indicarmi risorse dove posso imparare di più ?

Grazie.

+0

Sempre voluto fare questa domanda. – zedoo

+1

Ottima domanda! –

+1

Solo per chiarezza, questo codice viene eseguito in un browser o sul server in esecuzione come node.js? –

risposta

16

Non si dispone di una condizione di competizione, perché Javascript non viene eseguito contemporaneamente.

Ogni volta che viene attivata una richiamata da un'operazione asincrona (AJAX, setTimeout, ecc.), Tale callback deve terminare l'esecuzione prima che venga richiamata un'altra richiamata da un'altra operazione asincrona. Quindi, se update() è in esecuzione, nessun altro Javascript è. Una volta terminato lo update(), è possibile attivare altre callback da operazioni asincrone (ad esempio, tick()). È interessante notare che questo è anche il motivo per cui setTimeout e il suo ilk non sono garantiti per l'esecuzione nel preciso momento in cui viene raggiunto il timeout: alcuni altri javascript potrebbero bloccare l'esecuzione del callback.

Per ulteriori informazioni su come funziona tutto questo, consulta http://ejohn.org/blog/how-javascript-timers-work/.

-2

Fintanto che si aggiungono altri secondi all'orario corrente, si dovrebbe andare bene.

Invece di fare time = newtime; prova time += newtime; In questo modo non perderai il secondo di cui sei preoccupato.

Ancora, si sta perdendo solo un secondo nel peggiore dei casi.

+0

Nota, la nuova ora è solo quanto più tempo da aggiungere. –

+5

Non penso che questo risolva il problema delle condizioni di gara –

0

Ho sbagliato: questo non risolve il problema! (Spiegazione dopo il codice)

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

Il problema di questa soluzione sbagliata è che facendo in questo modo basta spostare il problema condizione di corsa da variabile time alla variabile NEWTIME.

Basti pensare questo: l'esecuzione di tick raggiunge ed esegue la linea time = NEWTIME; ora, prima di continuare, di aggiornamento e di ottenere chiamato NEWTIME ottiene un valore X. Ora l'esecuzione del segno di spunta continua con l'esecuzione di NEWTIME = false;. In questo modo hai perso il valore X di NEWTIME e quindi l'effetto di una chiamata update()!

+0

Questo è esattamente ciò che intendevo fare, in che modo non si tratta delle condizioni di gara? Potresti essere fuori da un ciclo, ma non mi sembra che 'NEWTIME' si perderà. –

+0

@Justin In questo modo si sposta il problema della condizione di gara dalla variabile 'time' alla variabile' NEWTIME'. Pensa solo a questo: l'esecuzione di tick raggiunge ed esegue la riga 'time = NEWTIME; 'ora, prima di continuare,' update' viene chiamato e 'NEWTIME' ottiene un valore' X'. Ora l'esecuzione 'tick' continua ad eseguire' NEWTIME = false; '. In questo modo hai perso l'effetto di una chiamata 'update()'! –

+0

Sì, ma lo si perde solo per un massimo di un tick, in contrasto con il modo originale che lo fa perdere per sempre * se * la lingua era concomitante. –

-1

Il problema richiede alcuni semafori. Lo faccio molto per mandare ajax dopo che il precedente finisce. Il tuo caso è un po 'simile;)

Prova qualcosa del genere. Dovrebbe ignorare tutti i tentativi di decremento del tempo che si scontrano con il callback di 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); 
}; 

Un'altra cosa - la setTimeout funziona come un nuovo thread e mi aspetto che i browser fanno la sincronizzazione accesso mem, quindi potrebbe essere sufficiente per controllare se il valore non è cresciuto prima di decremento. Ma la soluzione di cui sopra è più flessibile e sicura.

E un piccolo suggerimento - evitare l'esecuzione di query con AJAX troppo spesso - può causare ulteriori problemi - come le richieste provenienti in ordine diverso da quello inviato, e l'utilizzo della memoria firefox costruendo un sacco su troppo ajax un minuto;)

+0

Grazie. Cosa succede se il decremento fallisce, però? L'ora non viene visualizzata di nuovo. In questo caso, puoi semplicemente spostare la chiamata di riesecuzione al di fuori del se, ma per un caso più generale, c'è un modo non bloccante di attendere e riprovare in un momento? Inoltre, ho bisogno di controllare abbastanza frequentemente per gli aggiornamenti dal server, che al momento significa che sto facendo una chiamata ajax al secondo. Se ho bisogno di aggiornamenti frequenti, c'è un modo migliore per farlo rispetto a Ajax? Non c'è modo per il server di inviare una notifica ai client quando si verifica un evento, vero? – So8res

+0

Non ho tempo ora di controllare profondamente, ma questa non mi sembra una buona soluzione ... –

+2

Javascript è una sola discussione. Non hai bisogno di semafori. La cosa peggiore che si verifica è che si verifica il decremento (seguito da una nuova visualizzazione), quindi il valore viene immediatamente sovrascritto da un valore appena fornito dall'aggiornamento. In tal caso, l'ora verrà decrementata e visualizzata un secondo dopo. Non è un problema. – PanCrit

8

Javascript è singolo threaded. Non c'è condizione di gara. Non c'è modo in javascript per le due linee time = newtime; e time -= 1; da sovrapporre durante l'esecuzione. In effetti, le due funzioni sono garantite per non sovrapporsi. Uno di loro verrà eseguito e l'altro verrà eseguito.