1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
---
title: Modello di parallelismo ed Event Loop
slug: Web/JavaScript/EventLoop
translation_of: Web/JavaScript/EventLoop
---
<div>{{JsSidebar("Advanced")}}</div>
<div>Javascript ha un modello di parallelismo basato su un "event loop". Questo modello è abbastanza differente dai modelli degli altri linguaggi quali, per esempio, C e Java.</div>
<div></div>
<h2 id="Runtime">Runtime</h2>
<p>Questa sezione descrive un modello teorico. I moderni engine JavaScript implementano ed ottimizzano pesantemente la semantica descritta.</p>
<h3 id="Rappresentazione_grafica">Rappresentazione grafica</h3>
<p style="text-align: center;"><img alt="Stack, heap, queue" src="/files/4617/default.svg" style="height: 270px; width: 294px;"></p>
<h3 id="Stack">Stack</h3>
<p>Chiamate di funzioni da uno stack di <em>frames</em>.</p>
<pre class="brush: js">function f(b){
var a = 12;
return a+b+35;
}
function g(x){
var m = 4;
return f(m*x);
}
g(21);
</pre>
<p>Quando viene invocata <code>g</code>, viene creato un primo frame contenente gli argomenti di <code>g </code>e le variabili locali. Quando <code>g </code>invoca <code>f, </code>viene creato un secondo frame con un'operazione di push sopra il primo, contenente gli argomenti di <code>f</code> e le variabili locali. Quando <code>f </code>esce con il return, viene effettuata un'operazione di pop sullo stack (nel quale rimane quindi solo <code>g</code>). Quando anche <code>g</code> effettua il return, lo stack rimane vuoto.</p>
<h3 id="Heap">Heap</h3>
<p>L'heap indica una grande regione di memoria perlopiù non strutturata.</p>
<h3 id="Queue">Queue</h3>
<p>Un runtime JavaScript contiene una coda(queue) di messaggi, ossia una lista di messaggi da essere processati. Ad ogni messaggio è associata una funzione. Quando lo stack è vuoto, viene estratto un messaggio dalla coda e processato, ossia viene chiamata la funzione associata (e, pertanto, viene creato un frame nello stack). L'elaborazione dei messaggi finisce quando lo stack ritorna ad essere vuoto.</p>
<h2 id="Event_loop">Event loop</h2>
<p>L'event loop prende il suo nome dalla modalità con cui viene normalmente implementato, ossia tipicamente: </p>
<pre class="brush: js">while(queue.waitForMessage()){
queue.processNextMessage();
}</pre>
<p><code>queue.waitForMessage</code> attende che arrivi un messaggio quando non c'e' ne sono.</p>
<h3 id="Run-to-completion">"Run-to-completion"</h3>
<p>Ogni messaggio viene completamente elaborato prima di passare ad un altro. Questa dinamica offre alcune comode proprietà quando si ragiona sul proprio programma, incluso il fatto che, fino a che una funzione è in esecuzione, non c'è modo di bloccarla (pre-emption) e completerà la propria esecuzione prima che un altro codice possa essere eseguito (e possa manipolare i dati che la funzione manipola). Questo è completamente diverso dal C, ad esempio, dove, se una funzione è eseguita in un thread, può essere bloccata in qualsiasi momento per eseguire altro codice di un altro thread. </p>
<p>Un aspetto negativo di questo modello sta nel fatto che se un messaggio impiega troppo tempo ad essere processato, l'applicazione web non puà elaborare le interazioni dell'utente, come i click o gli scroll. Il browser in qualche modo mitiga questa situazione con la finestra "uno script sta impiegando troppo tempo a completare". Una buona pratica da seguire è di implementare l'elaborazione di un messaggio in modo che impieghi poco tempo e, se possibile, suddividere un messaggio in più messaggi.</p>
<h3 id="Aggiungere_messaggi">Aggiungere messaggi</h3>
<p>Nei browser web, i messaggi sono continuamente aggiunti ogni qualvolta si verifica un evento con un event listener associato. Quindi il click ad un elemento con un un handler associato aggiungerà un messaggio--così come ogni altro evento.</p>
<p>Una chiamata alla funzione <code><a href="/en-US/docs/Web/API/WindowTimers.setTimeout">setTimeout</a> </code>aggiunge un messaggio alla coda dopo che il tempo indicato come secondo argomento è trascorso. Se non c'è nessun altro messaggio nella coda, il messaggio è processato immediatamente; al contrario, se ci sono altri messaggi, il messaggio aggiunto da <code>setTimeout </code>dovrà attendere che gli altri messaggi vengano processati. Per questa ragione il secondo argomento di questa funzione indica un tempo minimo e non un tempo garantito.</p>
<h3 id="Intervallo_zero">Intervallo zero</h3>
<p>Zero delay non significa in realtà che la funzione di callback verrà attivata dopo zero millisecondi. Una chiamata a {{domxref("WindowTimers.setTimeout", "setTimeout")}} con un delay di 0 (zero) millisecondi, non esegue la funzione di callback dopo l'intervallo di tempo passato. L'esecuzione dipende dal numero di task in attesa nella coda. Nel seguente esempio il messaggio "this is just a message" verrà stampato a console prima che il messaggio della callback venga processato, perchè il ritardo è il minimo tempo richiesto per elaborare la richiesta, non un tempo garantito.</p>
<pre class="brush: js">(function () {
console.log('this is the start');
setTimeout(function cb() {
console.log('this is a msg from call back');
});
console.log('this is just a message');
setTimeout(function cb1() {
console.log('this is a msg from call back1');
}, 0);
console.log('this is the end');
})();
// "this is the start"
// "this is just a message"
// "this is the end"
// "this is a msg from call back"
// "this is a msg from call back1"
</pre>
<h3 id="Più_Runtime_in_comunicazione_tra_loro">Più Runtime in comunicazione tra loro</h3>
<p>Un web worker oppure un <code>iframe </code>cross-origin hanno i loro stack, heap e coda di messaggi. Due runtime distinti possono comunicare esclusivamente attraverso il metodo <code><a href="/en-US/docs/DOM/window.postMessage">postMessage</a>. </code>Questo metodo aggiunge il messaggio all'altro runtime se quest'ultimo è in ascolo degli eventi dei messaggi.</p>
<h2 id="Non_bloccante">Non bloccante</h2>
<p>Una proprietà molto interessante del modello event loop è che JavaScript, a differenza di molti altri linguaggi, non è mai bloccante. La gestione delle operazionei di I/O è tipicamente eseguita mediante eventi e callback, pertanto quando l'applicazione è in attesa che una query <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> restituisca il risultato oppure una richiesta di tipo <a href="/en-US/docs/Web/API/XMLHttpRequest">XHR</a> completi, può nel frattempo continuare a processare altre cose quali, ad esempio, gli user input.</p>
<p>Esistono delle eccezioni dovuti alla legacy quali <code>alert </code>oppure le richieste sincrone XHR, ma è considerata una buona pratica evitarle. Attenzione: esistono anche <a href="http://stackoverflow.com/questions/2734025/is-javascript-guaranteed-to-be-single-threaded/2734311#2734311">eccezioni alle eccezioni</a> (ma sono solitamente bachi implementativi o altro).</p>
|