2011-08-30 12 views
11

Supponiamo di caricare alcuni filmati Flash che, a un certo punto, in futuro chiameremo window.flashReady e imposterò window.flashReadyTriggered = true.Devo preoccuparmi delle condizioni di gara con Javascript asincrono?

Ora ho un blocco di codice che voglio aver eseguito quando il Flash è pronto. Voglio eseguirlo immediatamente se window.flashReady è già stato chiamato e voglio metterlo come callback in window.flashReady se non è stato ancora chiamato. L'approccio ingenuo è questo:

if(window.flashReadyTriggered) { 
    block(); 
} else { 
    window.flashReady = block; 
} 

Così la preoccupazione che ho sulla base di questo è che l'espressione nella condizione if viene valutata a false, ma poi prima di block() può essere eseguito, window.flashReady viene attivato dal flash esterno. Di conseguenza, block non viene mai chiamato.

Esiste un modello di progettazione migliore per raggiungere l'obiettivo di livello superiore che sto seguendo (ad esempio, chiamando manualmente la callback flashReady)? Se no, sono al sicuro, o ci sono altre cose che dovrei fare?

+0

Perché non basta che il flash chiami la funzione javascript? – zellio

risposta

13

JavaScript è singolo thread. Non ci sono condizioni di gara.

Quando non c'è più codice da eseguire al "puntatore di istruzioni" corrente, il "thread" "passa il testimone" e un gestore di eventi window.setTimeout in coda o il gestore di eventi possono eseguire il suo codice.

Otterrete una migliore comprensione dell'approccio single-threading di Javascript che legge le idee di progettazione di node.js.

Ulteriori approfondimenti: Why doesn't JavaScript support multithreading?

+0

Grazie per la risposta concisa che arriva dritto al punto. Suppongo fin dall'inizio che avrei dovuto formulare la mia domanda in questo modo. –

+0

Questa risposta non è la storia completa. Si prega di leggere la mia risposta qui sotto: http://stackoverflow.com/a/12799287/1040124 – Jens

+0

@Jens, gli eventi in entrata non prevarranno il codice corrente, ma hai ragione che ci sono delle gare su cui verrà attivato il callback. – kay

17

Tutti Javascript script del gestore di eventi sono gestiti dalla coda di evento di un master. Ciò significa che i gestori di eventi vengono eseguiti uno alla volta e uno viene eseguito fino al completamento prima che il successivo pronto per l'esecuzione inizi a essere eseguito. Come tale, non ci sono le tipiche condizioni di gara in Javascript che si vedrebbero in una lingua multithread in cui più thread della lingua possono essere eseguiti contemporaneamente (o time sliced) e conflitti per l'accesso alle variabili. Ogni singolo thread di esecuzione in javascript verrà eseguito fino al completamento prima del successivo. Ecco come funziona Javascript. Evento estratto dalla coda eventi, il codice inizia a essere in esecuzione per gestire quell'evento. Questo codice viene eseguito da solo fino a quando non restituisce il controllo al sistema in cui il sistema preleverà l'evento successivo dalla coda degli eventi ed eseguirà quel codice fino a quando non restituirà il controllo al sistema.

Quindi le condizioni di gara tipiche causate da due thread di esecuzione che vanno nello stesso momento non si verificano in Javascript.

Questo include tutte le forme di eventi JavaScript tra cui: eventi utente (mouse, i tasti, ecc ..), eventi timer, eventi di rete (callback Ajax), ecc ...

L'unico posto si può effettivamente fare multi-threading in Javascript è con il HTML5 Web Workers, ma sono molto isolati dal normale javascript (possono solo comunicare con javascript normale tramite il passaggio di messaggi) e non possono manipolare il DOM affatto e devono avere i propri script e spazio dei nomi, ecc ...


Anche se tecnicamente non lo chiamerei condizione di gara, ci sono ioni in Javascript a causa di alcune delle sue operazioni asincrone in cui è possibile eseguire due o più operazioni asincrone e potrebbe essere imprevedibile quando ciascuna operazione verrà completata rispetto alle altre.Questo crea un'incertezza di temporizzazione che (se il tempo relativo delle operazioni è importante per il tuo codice) crea qualcosa per cui devi codificarti manualmente. Potrebbe essere necessario sequenziare le operazioni in modo da eseguirne una e attendere letteralmente che si completi prima di iniziare la successiva. Oppure, puoi iniziare tutte e tre le operazioni e poi avere del codice che raccoglie tutti e tre i risultati e quando sono tutti pronti, il tuo codice procede.

In Javascript moderno, le promesse vengono generalmente utilizzate per gestire questi tipi di operazioni asincrone.

Quindi, se avete avuto tre operazioni asincrone che ogni ritorno una promessa (come la lettura da un database, il recupero di una richiesta da un altro server, ecc ...), si potrebbe sequenziare manualmente, allora in questo modo:

a().then(b).then(c).then(result => { 
    // result here 
}).catch(err => { 
    // error here 
}); 

O, se si voleva tutti a correre insieme (tutti in volo allo stesso tempo) e appena sanno quando sono stati tutti fatti, si potrebbe fare:

Promise.all([a(), b(), c()])..then(results => { 
    // results here 
}).catch(err => { 
    // error here 
}); 

Mentre io non chiamare queste condizioni di gara, sono nella stessa famiglia generale di progettare il tuo codice per controllare inde terminare il sequenziamento.


C'è un caso speciale che può verificarsi in alcune situazioni nel browser. Non è davvero una condizione di competizione, ma se stai usando molte variabili globali con stato temporaneo, potrebbe essere qualcosa di cui essere consapevole. Quando il proprio codice causa la comparsa di un altro evento, il browser a volte chiama tale gestore eventi in modo sincrono anziché attendere che venga eseguito il thread corrente di esecuzione. Un esempio di questo è:

  1. click
  2. i cambiamenti gestore di eventi click si concentrano su un altro campo
  3. che altro campo ha un gestore di eventi per onfocus
  4. del browser chiama il gestore di eventi onfocus immediatamente
  5. gestisce il gestore di eventi onfocus
  6. viene eseguito il resto del gestore di eventi click (dopo la chiamata .focus)

Questa non è tecnicamente una condizione di competizione perché è nota al 100% quando verrà eseguito il gestore di eventi onfocus (durante la chiamata .focus()). Ma, può creare una situazione in cui un gestore di eventi viene eseguito mentre un altro è nel mezzo dell'esecuzione.

+0

"Qualsiasi singolo thread di esecuzione" <--- che cos'è? E 'una singola iterazione del ciclo degli eventi? O è una singola esecuzione del gestore di eventi? – Vanuan

+1

È necessario definire le condizioni di gara. Le condizioni di gara non hanno nulla a che fare con i thread. – Vanuan

+0

@Vanuan - Perché c'è un solo pezzo di Javascript in esecuzione in un dato momento, non ci sono le tipiche condizioni di gara che si vedono con i thread in cui due thread cercano di accedere alla stessa variabile o il momento in cui un thread lo accede vs. un altro è del tutto imprevedibile. Poiché c'è solo un pezzo di javascript in esecuzione alla volta, quel tipo di condizioni di gara semplicemente non può accadere. Se vuoi chiedere un diverso tipo di condizione di gara, ti preghiamo di spiegare cosa stai chiedendo perché nessun altro in questa domanda apparentemente lo sta chiedendo. – jfriend00

7

È importante notare che si possono ancora sperimentare condizioni di gara se per es. utilizzare più XMLHttpRequest asincrona. Dove l'ordine delle risposte restituite non è definito (cioè le risposte potrebbero non tornare nello stesso ordine in cui sono state inviate). Qui l'output dipende dalla sequenza o dalla tempistica di altri eventi non controllabili (latenza del server ecc.). Questa è una condizione di gara in poche parole.

Quindi, anche l'utilizzo di una singola coda di eventi (come in JavaScript) non impedisce che eventi si verifichino in modo incontrollabile e il tuo codice dovrebbe occuparsene.

+1

Questa non è una condizione di gara, è solo il modo in cui funziona il codice asincrono. Se dai una scatola a Bob e dici "quando apri questa scatola, restituiscila a me", quindi passa immediatamente a Sue e dille la stessa cosa, a chi togli la scatola dal primo? Dipende da quanto tempo impiegano per aprire la scatola, ed è fuori dal tuo controllo. Allo stesso modo, una richiesta AJAX funziona allo stesso modo; non puoi presumere che completeranno in un ordine specifico perché non li ordini, stai semplicemente dicendo "al termine della richiesta, esegui questa funzione di callback". –

+3

Le condizioni di gara non devono necessariamente verificarsi con i thread, è semplicemente un modo per dire che hai 2 o più funzioni asincrone che competono per la stessa risorsa e non sai chi vincerà e quale sarà lo stato finale dell'esecuzione. Questo è il motivo per cui @Jens descritto è corretto –

-1

Certo che ti serve. Succede tutto il tempo:

<button onClick=function() { 
    const el = document.getElementById("view"); 
    fetch('/some/api').then((data) => { 
    el.innerHTML = JSON.stringify(data); 
    }) 
}>Button 1</button> 

<button onClick=function() { 
    const el = document.getElementById("view"); 
    fetch('/some/other/api').then((data) => { 
    el.innerHTML = JSON.stringify(data); 
    }) 

}>Button 2</button> 

Alcune persone non lo vedono come una condizione di gara.

Ma lo è davvero.

Le condizioni di gara sono generalmente definite come "il comportamento di un sistema elettronico, software o altro in cui l'uscita dipende dalla sequenza o dalla tempistica di altri eventi incontrollabili".

Se l'utente fa clic su questi 2 pulsanti in un breve periodo, l'output non dipende dall'ordine di clic. Dipende da quale richiesta API verrà risolta prima. Inoltre, l'elemento DOM a cui fai riferimento può essere rimosso da qualche altro evento (come cambiare rotta).

È possibile attenuare questa condizione di competizione disattivando il pulsante o mostrando alcuni spinner quando si carica un'operazione in corso, ma questo è imbroglio. Dovresti avere un mutex/contatore/semaforo a livello di codice per controllare il flusso asincrono.

Per adattarlo alla tua domanda, dipende da cosa è "block()". Se è una funzione sincrona, non devi preoccuparti. Ma se è asincrona, ci si deve preoccupare:

function block() { 
    window.blockInProgress = true; 
    // some asynchronous code 
    return new Promise(/* window.blockInProgress = false */); 
    } 

    if(!window.blockInProgress) { 
    block(); 
    } else { 
    window.flashReady = block; 
    } 

Questo codice ha senso si desidera impedire isolato da essere chiamato più volte. Ma se non ti interessa, o il "blocco" è sincrono, non ti devi preoccupare. Se sei preoccupato che un valore di variabile globale possa cambiare quando lo stai controllando, non dovresti preoccuparti, è garantito che non cambi a meno che non chiami una funzione asincrona.

Un esempio più pratico. Considera di voler memorizzare nella cache le richieste AJAX.

fetchCached(params) { 
    if(!dataInCache()) { 
    return fetch(params).then(data => putToCache(data)); 
    } else { 
    return getFromCache(); 
    } 
} 

Quindi succede se chiamiamo questo codice più volte? Non sappiamo quali dati verranno restituiti per primi, quindi non sappiamo quali dati verranno memorizzati nella cache. Le prime 2 volte restituiranno nuovi dati, ma la terza volta non conosciamo la forma della risposta da restituire.

+0

Questa non è una condizione di competizione, è un fraintendimento di come funziona il codice asincrono. Usando un esempio più standard, immagina se hai chiesto a due persone di andare alle loro scrivanie, prendi una penna e torna a metterla sulla tua scrivania, chi metterà la penna sulla scrivania prima? Non lo sai perché non li controlli. Forse un ragazzo correva e l'altro camminava. Forse un ragazzo usa prima il bagno. Questo non è diverso rispetto alla creazione di due pulsanti che eseguono richieste di rete. Quale completa prima? Non lo sai perché non controlli i tempi di risposta della rete. –

+0

Il malinteso consiste nell'aspettarsi che se si fa clic sul pulsante 1 e poi si preme il pulsante 2 poco dopo, quindi si dovrebbe restituire la prima risposta seguita dalla seconda. Questo non è diverso dal chiedere alla persona 1 prima di procurarmi una penna, quindi chiedere alla persona 2 di prendermi una penna poco dopo, e aspettare che la persona 1 mi faccia una penna per prima. –

+0

Facciamo una piccola modifica al tuo esempio. Una persona ha una penna rossa, un'altra persona ha una penna blu. Chiedi a queste due persone di mettere una penna sulla tua scrivania, ma se c'è già una penna, vuoi sostituirla. – Vanuan

Problemi correlati