JavaScript è garantito come single-threaded?

JavaScript è noto per essere a thread singolo in tutte le moderne implementazioni del browser, ma è specificato in qualsiasi standard o è solo per tradizione? È del tutto sicuro assumere che JavaScript sia sempre a thread singolo?

Questa è una bella domanda. Mi piacerebbe dire “sì”. Non posso

Solitamente, si considera che JavaScript abbia un singolo thread di esecuzione visibile agli script (*), in modo tale che quando lo script inline, il listener di eventi o il timeout viene inserito, si mantenga completamente il controllo fino al ritorno dalla fine del blocco o della funzione.

(*: ignorando la domanda se i browser implementano realmente i loro motori JS usando un thread OS, o se altri thread di esecuzione limitati sono introdotti da WebWorkers.)

Tuttavia, in realtà questo non è del tutto vero , in modi subdoli e cattivi.

Il caso più comune sono gli eventi immediati. I browser li triggersno immediatamente quando il codice fa qualcosa per causarli:

   

Risultati in log in, blur, log out su tutti tranne IE. Questi eventi non si triggersno solo perché hai chiamato focus() direttamente, potrebbero accadere perché hai chiamato alert() , o hai aperto una finestra pop-up, o qualsiasi altra cosa che sposta lo stato attivo.

Questo può anche portare ad altri eventi. Ad esempio, aggiungi un listener i.onchange e digita qualcosa nell’input prima che la chiamata a focus() lo decongestiona, e l’ordine del registro è log in, change, blur, log out , eccetto in Opera in cui è log in, blur, log out, change e IE dove è (anche meno esplicitamente) log in, change, log out, blur .

Allo stesso modo chiamare click() su un elemento che lo fornisce chiama immediatamente il gestore onclick in tutti i browser (almeno questo è coerente!).

(Sto usando il diretto on... proprietà del gestore di eventi qui, ma lo stesso accade con addEventListener e attachEvent .)

C’è anche un sacco di circostanze in cui gli eventi possono sparare mentre il tuo codice è inserito, nonostante tu non abbia fatto nulla per provocarlo. Un esempio:

    

Hit alert e otterrai una finestra di dialogo modale. Non si esegue più script fino a quando non si chiude quel dialogo, sì? No. Ridimensiona la finestra principale e riceverai alert in, resize, alert out nella textarea.

Potresti pensare che sia imansible ridimensionare una finestra mentre una finestra di dialogo modale è triggers, ma non così: in Linux, puoi ridimensionare la finestra quanto vuoi; su Windows non è così facile, ma puoi farlo cambiando la risoluzione dello schermo da una più grande a una più piccola dove la finestra non si adatta, causandone il ridimensionamento.

Si potrebbe pensare, beh, è ​​solo un resize (e probabilmente un po ‘più come scroll ) che può sparare quando l’utente non ha interazione triggers con il browser perché lo script è thread. E per le windows singole potresti avere ragione. Ma tutto va in pentola non appena esegui lo scripting cross-window. Per tutti i browser diversi da Safari, che blocca tutte le windows / tabulazioni / fotogrammi quando uno di essi è occupato, è ansible interagire con un documento dal codice di un altro documento, eseguendo in un thread separato di esecuzione e provocando qualsiasi gestore di eventi correlato a fuoco.

Luoghi in cui è ansible generare eventi che possono essere generati mentre lo script è ancora in corso:

  • quando i popup modali ( alert , confirm , prompt ) sono aperti, in tutti i browser tranne Opera;

  • durante showModalDialog sui browser che lo supportano;

  • la finestra di dialogo “Un copione su questa pagina potrebbe essere occupata …”, anche se si sceglie di lasciare che lo script continui a girare, consente agli eventi come ridimensionamento e sfocatura di essere gestiti anche mentre lo script si trova nel mezzo di un busy-loop, tranne in Opera.

  • qualche tempo fa per me, in IE con il Sun Java Plugin, chiamare qualsiasi metodo su un’applet poteva consentire agli eventi di sparare e allo script di essere reinserito. Questo è sempre stato un bug sensibile al tempo, ed è ansible che Sun lo abbia risolto da allora (lo spero di sicuro).

  • probabilmente di più È passato un po ‘di tempo da quando l’ho testato e da allora i browser hanno acquisito complessità.

In breve, per la maggior parte degli utenti, JavaScript appare per la maggior parte del tempo un rigoroso thread di esecuzione basato sugli eventi. In realtà, non ha niente del genere. Non è chiaro quanto di questo sia semplicemente un bug e quanta progettazione intenzionale, ma se stai scrivendo applicazioni complesse, specialmente quelle cross-window / frame-scripting, ci sono tutte le possibilità che potrebbe morderti – e in modo intermittente, modi difficili da debugare.

Se il peggio arriva al peggio, puoi risolvere i problemi di concorrenza seguendo le risposte di tutti gli eventi. Quando arriva un evento, rilasciarlo in una coda e gestire la coda in un secondo momento, in una funzione setInterval . Se stai scrivendo un framework che intendi utilizzare da applicazioni complesse, fare ciò potrebbe essere una buona mossa. postMessage allevia il dolore dello scripting cross-document in futuro.

Direi di sì – perché praticamente tutto il codice javascript esistente (almeno tutto non banale) si interromperebbe se il motore javascript di un browser dovesse eseguirlo in modo asincrono.

Inoltre, il fatto che HTML5 specifichi già i Web worker (un’API esplicita e standardizzata per il codice javascript multi-threading) l’introduzione del multi-threading nel Javascript di base sarebbe in gran parte inutile.

( Nota per altri commentatori: anche se setTimeout/setInterval , eventi onload richiesta HTTP (XHR) e eventi UI (clic, focus, ecc.) Forniscono un’impressione grossolana di multi-threadedness – sono ancora tutti eseguiti lungo una singola timeline – uno alla volta – quindi, anche se non conosciamo il loro ordine di esecuzione in anticipo, non c’è motivo di preoccuparsi delle condizioni esterne che cambiano durante l’esecuzione di un gestore di eventi, una funzione temporizzata o un callback XHR.)

Sì, anche se è ancora ansible riscontrare alcuni dei problemi legati alla programmazione simultanea (principalmente condizioni di competizione) quando si utilizza una delle API asincrone come callback setInterval e xmlhttp.

Sì, anche se Internet Explorer 9 compilerà il tuo Javascript su un thread separato in preparazione per l’esecuzione sul thread principale. Questo però non cambia nulla per te come programmatore.

In realtà, una finestra genitore può comunicare con windows figlio o fratelli o con frame che hanno i loro thread di esecuzione in esecuzione.

JavaScript / ECMAScript è progettato per vivere all’interno di un ambiente host. Cioè, JavaScript in realtà non fa nulla a meno che l’ambiente host non decida di analizzare ed eseguire uno script dato e fornisca oggetti di ambiente che consentono a JavaScript di essere effettivamente utile (come il DOM nei browser).

Penso che una determinata funzione o un blocco di script verranno eseguiti riga per riga e ciò è garantito per JavaScript. Tuttavia, forse un ambiente host potrebbe eseguire più script contemporaneamente. Oppure, un ambiente host potrebbe sempre fornire un object che fornisce multi-threading. setTimeout e setInterval sono esempi, o almeno pseudo-esempi, di un ambiente host che fornisce un modo per fare un po ‘di concorrenza (anche se non è esattamente concorrenza).

Direi che la specifica non impedisce a qualcuno di creare un motore che esegue javascript su più thread, richiedendo al codice di eseguire la sincronizzazione per accedere allo stato dell’object condiviso.

Penso che il paradigma non bloccante a thread singolo sia venuto dalla necessità di eseguire javascript nei browser in cui ui non dovrebbe mai bloccare.

Nodejs ha seguito l’approccio dei browser.

Tuttavia, il motore di Rhino supporta il codice js in esecuzione in diversi thread . Le esecuzioni non possono condividere il contesto, ma possono condividere l’ambito. Per questo caso specifico la documentazione afferma:

… “Rhino garantisce che gli accessi alle proprietà degli oggetti JavaScript sono atomici tra i thread, ma non offre più garanzie per gli script nell’esecuzione nello stesso ambito contemporaneamente. Se due script utilizzano lo stesso ambito contemporaneamente, gli script sono responsabile del coordinamento di eventuali accessi a variabili condivise . ”

Dalla lettura della documentazione di Rhino concludo che può essere ansible per qualcuno scrivere un javascript api che genera anche nuovi thread javascript, ma l’api sarebbe specifica per il rinoceronte (ad esempio, il nodo può solo generare un nuovo processo).

Immagino che anche per un motore che supporta più thread in javascript ci dovrebbe essere la compatibilità con script che non considerano il multi-threading o il blocco.

Concettualizzare i browser e i nodesjs come lo vedo io è:

  • Tutto il codice js è eseguito in un singolo thread? : Sì.

  • Il codice js può causare l’esecuzione di altri thread? : Sì.

  • Questi thread possono mutare il contesto di esecuzione di js ?: No. Ma possono (direttamente / indirettamente (?)) Accodare la coda degli eventi.

Quindi, in caso di browser e nodejs (e probabilmente molti altri motori) javascript non è multithread ma i motori stessi lo sono.

No.

Vado contro la folla qui, ma sopporta me. Un singolo script JS è pensato per essere efficacemente a thread singolo, ma ciò non significa che non possa essere interpretato diversamente.

Diciamo che hai il seguente codice …

 var list = []; for (var i = 0; i < 10000; i++) { list[i] = i * i; } 

Questo è scritto con l'aspettativa che entro la fine del ciclo, l'elenco deve avere 10000 voci che sono l'indice al quadrato, ma la VM potrebbe notare che ogni iterazione del ciclo non influenza l'altro, e reinterpretare usando due thread.

Primo thread

 for (var i = 0; i < 5000; i++) { list[i] = i * i; } 

Secondo thread

 for (var i = 5000; i < 10000; i++) { list[i] = i * i; } 

Sto semplificando qui, perché gli array JS sono più complicati di muti blocchi di memoria, ma se questi due script sono in grado di aggiungere voci all'array in un modo sicuro per i thread, allora quando entrambi finiranno, l'esecuzione avrà lo stesso risultato della versione a thread singolo.

Anche se non sono a conoscenza di alcuna macchina virtuale in grado di rilevare il codice parallelizzabile come questo, sembra probabile che potrebbe venire in essere in futuro per le macchine virtuali JIT, dal momento che potrebbe offrire maggiore velocità in alcune situazioni.

Prendendo ulteriormente questo concetto, è ansible che il codice possa essere annotato per consentire alla VM di sapere cosa convertire in codice multi-thread.

 // like "use strict" this enables certain features on compatible VMs. "use parallel"; var list = []; // This string, which has no effect on incompatible VMs, enables threading on // this loop. "parallel for"; for (var i = 0; i < 10000; i++) { list[i] = i * i; } 

Dal momento che i Web Worker stanno arrivando in Javascript, è improbabile che questo ... sistema più brutto possa mai esistere, ma penso che sia sicuro dire che Javascript è a thread singolo per tradizione.

@Bobince sta fornendo una risposta davvero opaca.

Rifugiando dalla risposta di Már Örlygsson, Javascript è sempre a thread singolo a causa di questo semplice fatto: tutto in Javascript viene eseguito lungo una singola timeline.

Questa è la definizione rigorosa di un linguaggio di programmazione a thread singolo.

Bene, Chrome è multiprocesso e penso che ogni processo abbia a che fare con il proprio codice Javascript, ma per quanto ne sa il codice è “single-threaded”.

Non c’è alcun supporto in Javascript per il multi-threading, almeno non esplicitamente, quindi non fa differenza.

Ho provato l’esempio di @ bobince con alcune modifiche:

   Test    

Quindi, quando premi Esegui, chiudi popup di avviso e fai un “singolo thread”, dovresti vedere qualcosa di simile a questo:

 click begin click end result = 20, should be 20 

Ma se provi a eseguire questo in Opera o Firefox stabile su Windows e riduci / ingrandisci la finestra con un popup di avviso sullo schermo, allora ci sarà qualcosa di simile a questo:

 click begin resize click end result = 15, should be 20 

Non voglio dire che questo è “multithreading”, ma un pezzo di codice è stato eseguito in un momento sbagliato con me non me lo aspettavo, e ora ho uno stato corrotto. E meglio sapere di questo comportamento.

Prova ad annidare due funzioni setTimeout l’una nell’altra e si comporteranno in multithreading (cioè il timer esterno non aspetterà che quello interno si completi prima di eseguire la sua funzione).