You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 2-ui/99-ui-misc/03-event-loop/article.md
+33-33Lines changed: 33 additions & 33 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,11 +1,11 @@
1
1
2
2
# Event loop: microtasks e macrotasks
3
3
4
-
Il flusso di esecuzione di Javascript, così come quello di Node.js, sono basati sull' *event loop*.
4
+
Il flusso di esecuzione di Javascript, così come quello di Node.js, è basato sull' *event loop*.
5
5
6
-
Comprendere come un event loop lavora è importante per le ottimizzazioni, ed a volte, anche per creare delle architetture migliori.
6
+
Comprendere come funziona un event loop è importante sia per ottimizzazione, ma a volte, anche per creare delle architetture migliori.
7
7
8
-
In questo capitolo affronteremo i dettagli teorici su come funziona, dopodichè prenderemo in esame alcune applicazioni pratiche.
8
+
In questo capitolo affronteremo i dettagli teorici sul funzionanento, dopodichè prenderemo in esame alcune applicazioni pratiche.
9
9
10
10
## Event Loop
11
11
@@ -20,46 +20,46 @@ Questa è una esposizione di quello che vediamo quando navighiamo una pagi
20
20
21
21
Esempio di tasks:
22
22
23
-
- Quando uno script esterno `<script src="...">` viene caricato (load), il task è quello di eseguirlo.
23
+
- Quando uno script esterno `<script src="...">` viene caricato (load), il task è quello di eseguirlo.
24
24
- Quando un utente sposta il puntatore del mouse, il task è quello di fare il dispatch dell'evento `mousemove` ed eseguirne eventuali handlers (gestori).
25
-
- Quando è scaduto il tempo per `setTimeout`già schedulato, il task è quello di eseguirne la callback.
25
+
- Quando è scaduto il tempo per `setTimeout`già schedulato, il task è quello di eseguirne la callback.
26
26
- ...e così via.
27
27
28
28
I task vengono impostati -- il motore li gestisce -- quindi rimane in attesa per altri tasks (nel frattempo rimane in sleep, consumando risorse della CPU prossime allo zero).
29
29
30
-
Però potrebbe succedere che mentre il motore è occupato, arrivi un task, in questo caso, questo viene messo in coda.
30
+
Però potrebbe succedere che mentre il motore è occupato, arrivi un task, in questo caso, questo viene messo in coda.
31
31
32
32
I task formano una coda, la cosiddetta "macrotask queue" (termine mutuato V8, il motore Javascript di Chrome e di Node.js):
33
33
34
34

35
35
36
-
Ad esempio, se mentre il motore è occupato nell'esecuzione di uno `script`, l'utente muove il mouse generando un `mousemove`, e magari nello stesso istante è scaduto il tempo di un `setTimeout`, questi task formano una queue (una coda di esecuzione) come illustrato nella figura di sopra.
36
+
Ad esempio, se mentre il motore è occupato nell'esecuzione di uno `script`, l'utente muove il mouse generando un `mousemove`, e magari nello stesso istante è scaduto il tempo di un `setTimeout`, questi task formano una queue (una coda di esecuzione) come illustrato nella figura di sopra.
37
37
38
-
I task dalla coda vengono processati sulla base del "first come – first served", cioè secondo l'ordine che il primo arrivato sarà il primo ad essere servito (FIFO).
39
-
Quando il motere del browser avrà terminato con lo `script`, gestirà l'evento `mousemove`, quindi si occuperà del gestore del `setTimeout` (la callback), e così
38
+
I task dalla coda vengono processati sulla base del "first come – first served", cioè secondo l'ordine che il primo arrivato sarà il primo ad essere servito (FIFO).
39
+
Quando il motere del browser avrà terminato con lo `script`, gestirà l'evento `mousemove`, quindi si occuperà del gestore del `setTimeout` (la callback), e così
40
40
41
41
Fino a qui abbastanza semplice, giusto?
42
42
43
43
Ancora due dettagli:
44
44
1. Il rendering non avviene mai quando il motore sta eseguendo un task. Non importa se questo impiega molto tempo. I cambiamenti al DOM vengono renderizzati (ridisegnati sul browser) solo dopo che il task viene completato.
45
-
2. Se un task impiega troppo tempo, il browsere non puiò eseguire altri taskm, processare eventi utente, e così dopo un certo periodo di tempo viene scaturito un alert di "Pagina bloccata"(Page Unresponsive) che ci suggerisce di terminare il task con l'intera pagina. Questo succede in concomitanza di una serie di calcoli complessi, o degli errori di programmazione che portano ad un loop infinito.
45
+
2. Se un task impiega troppo tempo, il browsere non puiò eseguire altri taskm, processare eventi utente, e così dopo un certo periodo di tempo viene scaturito un alert di "Pagina bloccata"(Page Unresponsive) che ci suggerisce di terminare il task con l'intera pagina. Questo succede in concomitanza di una serie di calcoli complessi, o degli errori di programmazione che portano ad un loop infinito.
46
46
47
47
Questa era la teoria. Adesso vediamo come applicare questi concetti.
48
48
49
49
## Caso d'uso 1: Spezzettamento di task affamati di CPU (processi intensivi)
50
50
51
51
Poniamo il caso che abbiamo un task affamato di CPU.
52
52
53
-
Per esempio, la syntax-highlighting (usata per colorare ed evidenziare gli esempi del codice in questa pagina) è abbastanza pesante per la CPU.
54
-
Per evidenziare il codice, compie delle analisi, crea molti elementi colorati, e li aggiunge al documento -- un testo di grosse dimensioni può impiegare molto tempo.
53
+
Per esempio, la syntax-highlighting (usata per colorare ed evidenziare gli esempi del codice in questa pagina) è abbastanza pesante per la CPU.
54
+
Per evidenziare il codice, compie delle analisi, crea molti elementi colorati, e li aggiunge al documento -- un testo di grosse dimensioni può impiegare molto tempo.
55
55
56
56
Mentre il motore è occupato con l'evidenziatura, non può fare le altre cose relative al DOM, processare gli eventi dell'utente, etc. può persino causare "singhiozzamenti" al pc o addirittura "inchiodarlo", la qual cosa è inaccettabile.
57
57
58
-
Possiamo quindi tirarci fuori da questo tipo di problemi, spezzettando i task grossi in piccoli pezzi. Evidenzia le prime 100 righe, quindi schedula un `setTimeout` (con zero-delay) con altre 100 righe, e così via.
58
+
Possiamo quindi tirarci fuori da questo tipo di problemi, spezzettando i task grossi in piccoli pezzi. Evidenzia le prime 100 righe, quindi schedula un `setTimeout` (con zero-delay) con altre 100 righe, e così via.
59
59
60
-
Per dimostrare questo tipo di approccio, per amore della semplicità, invece di evidenziare una sintassi, prendiamo una funzione che conti i numeri da `1` a `1000000000`
60
+
Per dimostrare questo tipo di approccio, per amore della semplicità, invece di evidenziare una sintassi, prendiamo una funzione che conti i numeri da `1` a `1000000000`
61
61
62
-
Se esegui il codice sotto, il motore si inchioderà per qualche istante. Per il JS server-side (lato server) questo è chiaramente visibile, e se lo stai eseguendo nella finestra del browser, prova a cliccare gli altri pulsanti -- potrei notare che non verrà gestito nessun altro evento fino a quando il conto non sarà terminato.
62
+
Se esegui il codice sotto, il motore si inchioderà per qualche istante. Per il JS server-side (lato server) questo è chiaramente visibile, e se lo stai eseguendo nella finestra del browser, prova a cliccare gli altri pulsanti -- potrei notare che non verrà gestito nessun altro evento fino a quando il conto non sarà terminato.
63
63
64
64
```js run
65
65
let i =0;
@@ -113,7 +113,7 @@ Una singola esecuzione di `count` fa una parte dell'operazione `(*)`, e rischedu
113
113
114
114
Ora, se arriva un nuovo task da eseguire mentre il motore è occupato ad eseguire il passo 1 (ad esempio un evento `onclick`), quest'ultimo viene messo in coda e viene eseguito subito dopo il completamento del passo 1, e subito prima del passo successivo. Questi periodici "ritorni" all'event loop tra una esecuzione di `count` e l'altra, fornisce abbastanza "aria" al motore Javascript per occuparsi di qualcos'altro, ad esempio per reagire alle azioni degli utenti.
115
115
116
-
La cosa ragguardevole è che entrambe le varianti -- con e senza la divsione del lavoro di `setTimeout` -- sono comparabili in termini di tempo. Complessivamente, non esiste molta differenza nel tempo di conteggio.
116
+
La cosa ragguardevole è che entrambe le varianti -- con e senza la divsione del lavoro di `setTimeout` -- sono comparabili in termini di tempo. Complessivamente, non esiste molta differenza nel tempo di conteggio.
117
117
118
118
Per renderli un po' più facciamo un miglioramento.
119
119
@@ -140,21 +140,21 @@ function count() {
140
140
count();
141
141
```
142
142
143
-
Adesso quando cominciamo con `count()` e vediamo che abbiamo bisogno di richiamarlo più`count()`, lo scheduliamo subito, prima di fare il lavoro.
143
+
Adesso quando cominciamo con `count()` e vediamo che abbiamo bisogno di richiamarlo più`count()`, lo scheduliamo subito, prima di fare il lavoro.
144
144
145
145
Se lo esegui, è facile notare che impiega meno tempo in modo significativo.
146
146
147
147
148
148
Perchè?
149
149
150
-
Semplice: come ben sai, c'è un ritardo minimo di 4ms all'interno del browser per molte chiamate annidate di `setTimeout`. Anche se noi lo settiamo `0`, sarà di `4ms` (o qualcosa in più). Quindi, prima lo scheduliamo, più veloce sarà l'esecuzione.
150
+
Semplice: come ben sai, c'è un ritardo minimo di 4ms all'interno del browser per molte chiamate annidate di `setTimeout`. Anche se noi lo settiamo `0`, sarà di `4ms` (o qualcosa in più). Quindi, prima lo scheduliamo, più veloce sarà l'esecuzione.
151
151
Alla fine, abbiamo diviso un task affamato di CPU in porzioni - e adesso non bloccherà l'interfaccia utente. E il suo tempo di esecuzione complessivo non è tanto più lungo.
152
152
153
153
## Caso d'uso 2: Indicazione dei progressi di una operazione
154
154
155
-
Un altro beneficio di dividere task pesanti per gli script del browser è che possiamo mostrare i progressi di completamento.
155
+
Un altro beneficio di dividere task pesanti per gli script del browser è che possiamo mostrare i progressi di completamento.
156
156
157
-
Solitamente il browser renderizza dopo che il codice in esecuzine viene completato. Non importa se il task impiega tanto tempo. Le modifice al DOM vengono mostrate solo dopo che il task è terminato.
157
+
Solitamente il browser renderizza dopo che il codice in esecuzine viene completato. Non importa se il task impiega tanto tempo. Le modifice al DOM vengono mostrate solo dopo che il task è terminato.
158
158
159
159
Da una parte, questo è grandioso, perchè la nostra fuznione può molti elementi, aggiungerli uno alla volta al documento e cambiarne gli stili -- il visitatore non vorrebbe mai vedere uno stadio "intermedio", incompleto. Una cosa importante, giusto?
160
160
@@ -180,7 +180,7 @@ Qui c'è la demo, i cambiamenti a `i` non verrano mostrati fino a quando l
180
180
181
181
Se noi dividiamo il task pesante in pezzi usando `setTimeout`, allora tra essi, verranno mostrate le variazioni.
182
182
183
-
Questo sembra più carino:
183
+
Questo sembra più carino:
184
184
185
185
```html run
186
186
<divid="progress"></div>
@@ -214,7 +214,7 @@ Adesso il `<div>` mostra via via, valori crescenti di `i`,come se fosse una sort
214
214
In un gestore di evento, potremmo decidere di postporre alcune azioni, fino a che l'evento non risalga i vari livelli di stack (bubbling up) e non venga gestito su tutti questi livelli.
215
215
Possiamo farlo, avvolgendo (wrapping) il codice all'interno dei `setTimeout` a ritardo zero.
216
216
217
-
Nel capitolo <info:dispatch-events> abbiamo visto un esempio: dell'evento custom `menu-open`, viene fatto il dispatch dentro `setTimeout`, così che esso viene richiamato dopo che l'evento click è stto del tutto gestito.
217
+
Nel capitolo <info:dispatch-events> abbiamo visto un esempio: dell'evento custom `menu-open`, viene fatto il dispatch dentro `setTimeout`, così che esso viene richiamato dopo che l'evento click è stto del tutto gestito.
218
218
219
219
220
220
```js
@@ -255,11 +255,11 @@ alert("code");
255
255
256
256
Che sta succedendo all'ordine qui?
257
257
258
-
1.`code` viene mostrato prima, dato che è è un chiamata regolare e sincrona.
258
+
1.`code` viene mostrato prima, dato che è un chiamata regolare e sincrona.
259
259
2.`promise` viene mostrato per secondo, perchè`.then` passa attraverso la coda di microtask, e viene eseguito dopo il codice corrente.
260
-
3.`timeout` viene mostrato come ultimo perchèè anhe questo un microtask.
260
+
3.`timeout` viene mostrato come ultimo perchèè anhe questo un microtask.
261
261
262
-
L'immagine più esausitva di un event loop èè questa:
262
+
L'immagine più esausitva di un event loop èè questa:
263
263
264
264

265
265
@@ -300,22 +300,22 @@ L'immagine più esausitva di un event loop è questa:
300
300
301
301

302
302
303
-
Il più dettagliato algoritmo dell'event loop (sebbene ancora semplicistico rispetto alla [specification](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)):
303
+
Il più dettagliato algoritmo dell'event loop (sebbene ancora semplicistico rispetto alla [specification](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)):
304
304
305
-
1. Rimuovi dalla coda ed esegui, il più vecchio task dalla coda dei *macrotask* (ad esempio "script").
305
+
1. Rimuovi dalla coda ed esegui, il più vecchio task dalla coda dei *macrotask* (ad esempio "script").
306
306
2. Eseguit tutti i *microtasks*:
307
-
- Se la cosa dei microtask non è vuota:
308
-
- Rimuovi dalla coda ed esegui il più vecchio dei microtask.
307
+
- Se la cosa dei microtask non è vuota:
308
+
- Rimuovi dalla coda ed esegui il più vecchio dei microtask.
309
309
3. Renderizza le modifiche se ve ne sono.
310
-
4. Se la coda dei macrotask è vuota, vai in sleep fino al prossimo macrotask.
310
+
4. Se la coda dei macrotask è vuota, vai in sleep fino al prossimo macrotask.
311
311
5. Vai al passo 1.
312
312
313
313
Per schedulare un nuovo *macrotask*:
314
314
- Use zero delayed `setTimeout(f)`.
315
315
316
316
Questo potrebbe essere usato per divitere task di calcolopesante in pezzi, di modo che tra questi, il browser possa eseguire altre operazioni.
317
317
318
-
Inoltre, vengono usati nei gestori degli eventi per schedulre una azione dopo che l'evento è stato del tutto gestito (bubbling completato)
318
+
Inoltre, vengono usati nei gestori degli eventi per schedulre una azione dopo che l'evento è stato del tutto gestito (bubbling completato)
319
319
320
320
Per schedulare un nuovo *microtask*
321
321
- Usa `queueMicrotask(f)`.
@@ -328,9 +328,9 @@ Quindi uno potrebbe volere che la coda `queueMicrotask` eseguisse una funzione i
328
328
```smart header="Web Workers"
329
329
Per lunghi calcoli pesanti che non possono bloccare l'event loop, possiamo usare i [Web Workers](https://html.spec.whatwg.org/multipage/workers.html).
330
330
331
-
Che è un modo per eseguire del codice in un altro thread parallelo.
331
+
Che è un modo per eseguire del codice in un altro thread parallelo.
332
332
333
333
I Web Workers possono scambiarsi messaggi con il processo principale, ma hanno le loro variabili ed i loro event loop.
334
334
335
-
I Web Workers non hanno accesso al DOM, quindi sono adatti principalmente per i calcoli, per usare contemporaneamente più cores CPU.
335
+
I Web Workers non hanno accesso al DOM, quindi sono adatti principalmente per i calcoli, per usare contemporaneamente più cores CPU.
0 commit comments