ciclo di eventi nel nodo js

Ciclo Di Eventi Nel Nodo Js



Node.js è un potente framework Javascript che consente agli utenti di eseguire codice Javascript sul server al di fuori del browser. Si tratta di un ambiente runtime non bloccante e basato sugli eventi per la creazione di applicazioni Web scalabili e affidabili. Il loop degli eventi è una parte importante di Node.js che ti consente di eseguire attività senza attendere che una finisca prima di iniziarne un'altra.

Sebbene Javascript sia un linguaggio a thread singolo, Node.js può assegnare attività al sistema operativo, consentendogli di elaborare più attività contemporaneamente. È necessario completare più attività contemporaneamente poiché le operazioni nel sistema operativo sono multi-thread. La richiamata associata a ciascuna operazione viene aggiunta alla coda degli eventi e viene pianificata da Node.js per l'esecuzione al completamento dell'attività specificata.

Per scrivere codice Node.js efficiente e affidabile, l'utente deve avere una solida conoscenza dei loop di eventi. Può anche aiutare a risolvere in modo efficace i problemi di prestazioni. Il ciclo di eventi in Node.js consente di risparmiare memoria e ti consente di fare più cose contemporaneamente senza dover attendere che finiscano ognuna. Il termine “asincrono” si riferisce a qualsiasi funzione Javascript che viene eseguita in background senza bloccare le richieste in entrata.







Prima di passare direttamente ai cicli di eventi, diamo uno sguardo ai diversi aspetti del linguaggio di programmazione Javascript.



Javascript come linguaggio di programmazione asincrono

Diamo uno sguardo ai concetti di programmazione asincrona. Javascript viene utilizzato nelle applicazioni web, mobili e desktop, ma va notato che Javascript è un linguaggio di programmazione per computer sincrono a thread singolo.



Viene fornito un semplice esempio di codice per comprendere il concetto.





metodo di funzione1 ( ) {

consolle. tronco d'albero ( 'Funzione 1' )

}

metodo di funzione2 ( ) {

consolle. tronco d'albero ( 'Funzione 2' )

}

metodo1 ( )

metodo2 ( )

In questo codice vengono create due semplici funzioni e viene chiamato per primo il metodo 1 in modo da registrare prima il metodo 1 e poi passare a quello successivo.

Produzione



Javascript come linguaggio di programmazione sincrono

Javascript è un linguaggio di programmazione sincrono ed esegue ogni riga passo dopo passo spostandosi dall'alto verso il basso con una sola riga eseguita alla volta. Nel codice di esempio fornito sopra il metodo1 viene registrato prima nel terminale e poi nel metodo2.

Javascript come linguaggio bloccante

Essendo un linguaggio sincrono, JavaScript ha una funzionalità di blocco. Non importa quanto tempo occorre per completare un processo in corso, un nuovo processo non verrà avviato finché quello precedente non sarà stato completato. Nell'esempio di codice sopra supponiamo che ci sia molto script di codice nel metodo1, non importa quanto tempo impiega 10 secondi o un minuto, il metodo2 non verrà eseguito finché tutto il codice nel metodo1 non sarà stato eseguito.

Gli utenti potrebbero averlo riscontrato durante la navigazione. Quando un'applicazione Web viene eseguita in un browser nel back-end, viene eseguita un'enorme porzione di codice, quindi il browser sembra essere congelato per un po' di tempo prima di restituire all'utente l'accesso di controllo. Questo comportamento è noto come blocco. Il browser non può accogliere ulteriori richieste in arrivo finché la richiesta corrente non è stata elaborata.

Javascript è un linguaggio a thread singolo

Per eseguire un programma in javascript viene utilizzata la funzionalità thread. I thread sono in grado di eseguire solo un'attività alla volta. Altri linguaggi di programmazione supportano il multi-threading e possono eseguire più attività in parallelo, Javascript contiene solo un thread per l'esecuzione di qualsiasi script di codice.

In attesa in Javascript

Come evidente dal nome in questa sezione, dobbiamo attendere che la nostra richiesta venga elaborata per procedere ulteriormente. L'attesa può durare diversi minuti durante i quali non vengono prese in considerazione ulteriori richieste. Se lo script del codice procede senza attendere, il codice rileverà un errore. Alcune funzionalità devono essere implementate in Javascript o più specificamente in Node.js per rendere il codice asincrono.

Ora che abbiamo compreso i diversi aspetti di Javascript, comprendiamo il sincrono e l'asincrono con alcuni semplici esempi.

Esecuzione sincrona di codice in Javascript

Sincrono significa che il codice viene eseguito in sequenza o più semplicemente passo dopo passo partendo dall'alto e scendendo riga per riga.

Di seguito viene riportato un esempio che può aiutare a comprendere:

// applicazione.js

consolle. tronco d'albero ( 'Uno' )

consolle. tronco d'albero ( 'Due' )

consolle. tronco d'albero ( 'Tre' )

In questo codice ci sono tre istruzioni console.log, ciascuna delle quali stampa qualcosa. Innanzitutto la prima istruzione che stamperà 'Uno' nella console viene inviata nello stack di chiamate per 1 ms (stimato), quindi viene registrata sul terminale. Successivamente, la seconda istruzione viene inserita nello stack di chiamate e ora il tempo è di 2 ms con l'aggiunta di uno dalla precedente e quindi registra 'Due' sulla console. Infine, l'ultima istruzione viene inserita nello stack di chiamate per ora il tempo è 3 ms e registra 'Tre' nella console.

Il codice sopra può essere eseguito invocando il seguente comando:

applicazione del nodo. js

Produzione

Il funzionamento è spiegato sopra in dettaglio e tenendolo in considerazione l'output viene registrato nella console in un batter d'occhio:

Esecuzione asincrona di codice in Javascript

Ora eseguiamo il refactoring dello stesso codice introducendo callback e rendendo il codice asincrono. Il codice sopra può essere refactoring come:

// applicazione.js
funzione stampaUno ( richiamare ) {
setTimeout ( funzione ( ) {
consolle. tronco d'albero ( 'Uno' ) ;
richiamare ( ) ;
} , 1000 ) ;
}
funzione stampaDue ( richiamare ) {
setTimeout ( funzione ( ) {
consolle. tronco d'albero ( 'Due' ) ;
richiamare ( ) ;
} , 2000 ) ;
}
funzione stampaTre ( ) {
setTimeout ( funzione ( ) {
consolle. tronco d'albero ( 'Tre' ) ;
} , 3000 ) ;
}
consolle. tronco d'albero ( 'Inizio del programma' ) ;
stampaUno ( funzione ( ) {
stampaDue ( funzione ( ) {
stampaTre ( ) ;
} ) ;
} ) ;
consolle. tronco d'albero ( 'Fine del programma' ) ;

In questo codice sopra:

  • Vengono dichiarate tre funzioni per stampare 'Uno', 'Due' e 'Tre', ciascuna funzione ha un parametro di callback che consente l'esecuzione sequenziale del codice.
  • Viene impostato un timeout utilizzando la funzione setTimeout ed è presente un'istruzione console.log per la stampa dopo un ritardo specifico.
  • Vengono stampati due messaggi “Inizio Programma” e “Fine Programma” che indicano l'inizio e la fine del programma.
  • Il programma inizia stampando 'Avvio del programma', dopodiché la funzione printOne viene eseguita con un ritardo di 1 secondo, quindi la funzione printTwo viene eseguita con un ritardo di 2 secondi e infine la funzione printThree viene eseguita con un ritardo di 3 secondi. ritardo.
  • Il programma non attende l'esecuzione del codice asincrono all'interno delle funzioni setTimeouts che registrano l'istruzione 'Fine del programma' prima di stampare Uno, Due e Tre.

Produzione

Esegui il codice sopra eseguendo questo comando nel terminale:

applicazione del nodo. js

Ora l'output nel terminale verrà visualizzato in modo asincrono come:

Ora che abbiamo una comprensione completa dell'esecuzione sincrona e asincrona passiamo a consolidare il nostro concetto di loop di eventi in Node.js.

Node.js: meccanismo di loop degli eventi

L'esecuzione di attività sia sincrone che asincrone è gestita dal loop di eventi in Node.js. L'esecuzione viene richiamata non appena il progetto Node.js viene avviato e trasferisce senza problemi le attività complesse al sistema. Ciò garantisce che altre attività possano essere eseguite senza problemi sul thread principale.

Spiegazione visiva del loop di eventi in Node.js

Il ciclo degli eventi è continuo e semi-infinito in Node.js. Il loop di eventi viene richiamato dall'inizio dello script di codice Node.js ed è responsabile dell'esecuzione di chiamate API asincrone e della chiamata di processi.Tick(), quindi la pianificazione dei timer riprende l'esecuzione del loop di eventi.

In Node.js, cinque tipi principali di code gestiscono i callback:

  • La 'coda timer' comunemente nota come min-heap è responsabile della gestione dei callback associati a 'setTimeout' e 'setInterval'.
  • I callback per le operazioni asincrone come nei moduli “fs” e “http” sono gestiti da “I/O Queue”.
  • Il 'Check Queue' contiene callback per la funzione 'setImmediate' che è univoca per Node.
  • La 'Coda di chiusura' gestisce i callback associati all'evento di chiusura di qualsiasi attività asincrona.
  • Infine, ci sono due code diverse nella coda “Micro Task”:
    • La coda 'nextTick' contiene callback associati alla funzione 'process.nextTick'.
    • La coda 'Promise' controlla le richiamate relative alla Promise nativa.

Funzionalità Event Loop in Node.js

Il ciclo di eventi funziona in base a requisiti specifici che controllano l'ordine di esecuzione della richiamata. Al codice Javascript sincrono dell'utente viene data priorità all'inizio del processo, quindi il ciclo di eventi inizia solo quando lo stack di chiamate viene cancellato. La seguente sequenza di esecuzione segue uno schema strutturato:

La priorità più alta viene data alle richiamate nella coda microtask, quindi si passa all'esecuzione delle attività nella coda nextTick seguite dalle attività nella coda Promise. I processi nelle richiamate della coda del timer vengono quindi gestiti, dopodiché la coda delle microtask viene rivisitata dopo ogni richiamata del timer. Le richiamate nelle code di I/O, controllo e chiusura vengono quindi eseguite in uno schema simile con la coda dei microtask visitata dopo ogni fase.

L'esecuzione del ciclo continua se sono presenti più callback da elaborare. Quando lo script del codice è terminato o non rimangono callback da elaborare, il ciclo di eventi termina in modo efficiente.

Ora che comprendiamo a fondo il ciclo degli eventi, diamo un'occhiata alle sue caratteristiche.

Funzionalità del loop di eventi in Node.js

Le caratteristiche principali sono:

  • Il ciclo degli eventi è un ciclo infinito e continua a eseguire le attività non appena le riceve e va in modalità sospensione nel caso in cui non siano presenti attività, ma inizia a funzionare non appena l'attività viene ricevuta.
  • Le attività nella coda degli eventi vengono eseguite solo quando lo stack è vuoto, ovvero non vi è alcuna operazione attiva.
  • Callback e promesse possono essere utilizzati nel ciclo degli eventi.
  • Poiché il ciclo degli eventi segue il principio della coda del tipo di dati astratto, soddisfa la prima attività e poi procede a quella successiva.

Dopo una conoscenza approfondita del ciclo degli eventi e della logica delle esecuzioni asincrone e sincrone, comprendere le diverse fasi può consolidare i concetti del ciclo degli eventi.

Fasi del loop di eventi Node.js

Come accennato in precedenza, il ciclo degli eventi è semiinfinito. Ha molte fasi ma alcune fasi vengono utilizzate per la gestione interna. Queste fasi non hanno alcun effetto sullo script del codice.

Il ciclo degli eventi segue la funzionalità di Queue ed esegue l'attività in base al principio first-in e first-out. I timer programmati verranno gestiti dal sistema operativo fino alla loro scadenza. I timer scaduti vengono quindi aggiunti alla coda di richiamata per i timer.

Il ciclo degli eventi esegue le attività nella coda del timer una alla volta fino a quando non rimangono più attività o fino a raggiungere il numero massimo consentito di attività. Nelle sezioni seguenti vengono spiegate le fasi principali dei loop di eventi.

Fase dei timer

In Node.js c'è un'API timer che può pianificare le funzioni che devono essere eseguite in futuro. Una volta trascorso il tempo assegnato, la richiamata del timer verrà eseguita non appena sarà possibile programmarla; tuttavia, è possibile che si verifichi un ritardo a livello del sistema operativo o dovuto all'esecuzione di altri callback.

L'API dei timer ha tre funzioni principali:

  • setTimeout
  • setImmediato
  • setInterval

Le funzioni sopra menzionate sono sincrone. La fase del timer nel ciclo degli eventi ha il suo ambito limitato alle funzioni setTimeout e setInterval. Mentre la funzione check gestisce la funzione setImmediate.

Consideriamo un semplice esempio per consolidare la parte teorica:

// applicazione.js

funzione ritardataFunzione ( ) {

consolle. tronco d'albero ( 'la funzione ritardata viene eseguita dopo il timeout' ) ;

}

consolle. tronco d'albero ( 'Inizio del programma' ) ;

setTimeout ( ritardatoFunzione, 2000 ) ;

consolle. tronco d'albero ( 'Fine del programma' ) ;

In questo codice:

  • Il programma si avvia registrando sul terminale la dichiarazione “Avvio del programma”.
  • Quindi la delayFunction viene chiamata con un timer di 2ms, lo script di codice non si ferma e procede ulteriormente gestendo il ritardo in background.
  • L'istruzione “Fine del programma viene registrata dopo la prima istruzione.
  • Dopo un ritardo di 2 ms, l'istruzione nella delayFunction viene registrata sul terminale.

Produzione

L'output verrà visualizzato come:

Si può vedere che il codice non viene interrotto per l'elaborazione della delayFunction; va avanti e, trascorso il ritardo, viene elaborata la richiamata della funzione.

Richiamate in sospeso

Il ciclo degli eventi controlla gli eventi che si verificano, come la lettura di file, attività di rete o attività di input/output, nella fase di polling. È importante sapere che in Node.js solo alcuni eventi vengono gestiti in questa fase di polling. Tuttavia, nella successiva iterazione del ciclo degli eventi, alcuni eventi potrebbero essere rinviati alla fase pendente. Questo è un concetto chiave da tenere presente durante l'ottimizzazione e la risoluzione dei problemi del codice Node.js che coinvolge operazioni complesse guidate da eventi.

È importante capire che durante la fase di attesa delle callback, il ciclo degli eventi aggiunge eventi posticipati alla coda delle callback pendenti e li esegue. Questa fase gestisce anche alcuni errori del socket TCP generati dal sistema, come gli eventi di errore ECONNREFUSED su determinati sistemi operativi.

Di seguito viene menzionato un esempio per consolidare il concetto:

// applicazione.js
cost fs = richiedere ( 'fs' ) ;
funzione readFileAsync ( filePath, richiamata ) {
fs. leggiFile ( './PromiseText.txt' , 'utf8' , funzione ( ehm, dati ) {
Se ( errare ) {
consolle. errore ( ` Errore file di lettura : $ { errare. Messaggio } ` ) ;
} altro {
consolle. tronco d'albero ( ` File contenuto : $ { dati } ` ) ;
}
richiamare ( ) ;
} ) ;
}
consolle. tronco d'albero ( 'Inizio del programma' ) ;
leggiFileAsync ( './PromiseText.txt' , funzione ( ) {
consolle. tronco d'albero ( 'Richiamata di lettura file eseguita' ) ;
} ) ;
consolle. tronco d'albero ( 'Fine del programma' ) ;

In questo codice:

  • Il programma viene avviato registrando la dichiarazione 'Avvio del programma' nel terminale.
  • Il readFileAsync è definito in modo asincrono per leggere il contenuto del file “PromiseText.txt”. È una funzione parametrizzata che esegue una funzione di callback dopo che il file è stato letto.
  • La funzione readFileAsync viene chiamata per iniziare il processo di lettura del file.
  • Durante il processo di lettura del file, il programma non si ferma; procede invece all'istruzione successiva e la registra nel terminale “Fine del programma”.
  • L'evento asincrono di lettura del file viene elaborato in background dal loop degli eventi.
  • Dopo che il file è stato letto in modo asincrono e il contenuto è stato registrato sul terminale, il programma registra il contenuto del file sul terminale. Successivamente, registra il seguente messaggio 'Richiamata di lettura file eseguita'.
  • Il ciclo di eventi gestisce le operazioni di callback in sospeso nella fase successiva.

Produzione

Il risultato dell'esecuzione di cui sopra è:

Inattivo, fase di preparazione in Node.js

La fase inattiva viene utilizzata per gestire le funzioni interne in Node.js, quindi non è una fase standard. Non influenza lo script del codice. La fase di inattività è come un periodo di pausa per il ciclo degli eventi durante il quale gestisce in background le attività a bassa priorità. Un semplice esempio per comprendere questa fase è:

cost { oziare } = richiedere ( 'idle-gc' ) ;

oziare. ignorare ( ) ;

In questo codice viene utilizzato il modulo “idle-gc” che permette di ignorare la fase di idle. Ciò serve a gestire le situazioni in cui il loop degli eventi è occupato e le attività in background non vengono eseguite. L'uso di idle.ignore non è considerato ottimale in quanto può causare problemi di prestazioni.

Fase di polling in Node.js

La fase di sondaggio in Node.js funge da:

  • Gestisce gli eventi nella coda del poll ed esegue le attività corrispondenti.
  • Decide quanto tempo dedicare all'attesa e al controllo delle operazioni di I/O nel processo.

Quando il loop di eventi entra nella fase di poll a causa dell'assenza di un timer, verrà eseguita una delle attività seguenti:

  • Nella fase di poll del loop di eventi in Node.js, gli eventi I/O pendenti vengono accodati e quindi eseguiti in una procedura sequenziale seguendo il principio First In e First Out finché la coda non si svuota. Durante l'esecuzione delle callback sono in azione anche le code nextTick e microtask. Ciò garantisce fluidità e consente di gestire le operazioni di I/O in modo più efficiente e affidabile.
  • Se la coda è vuota e lo script non è stato pianificato dalla funzione setImmediate() allora il ciclo degli eventi terminerà e si procederà alla fase successiva (controllo). D'altra parte, se la pianificazione dello script è stata eseguita dalla funzione setImmediate(), il ciclo di eventi consente di aggiungere i callback alla coda che verrà eseguita da esso.

Questo è meglio illustrato con un semplice esempio di codice:

setTimeout ( ( ) => {

consolle. tronco d'albero ( 'Operazione asincrona completata' ) ;

} , 2000 ) ;

consolle. tronco d'albero ( 'Inizio' ) ;

setImmediato ( ( ) => {

consolle. tronco d'albero ( 'setImmediate callback eseguito' ) ;

} ) ;

consolle. tronco d'albero ( 'FINE' ) ;

In questo codice:

  • Due messaggi “Start” e “End” indicano l'inizio e la conclusione del programma.
  • La funzione setTimeout() imposta una funzione di callback con un ritardo di 2 ms e registra 'Operazione asincrona completata' sul terminale.
  • La funzione setImmediate() registra il messaggio 'setImmediate callback eseguito' sul terminale dopo che il messaggio di avvio è stato registrato sul terminale.

Produzione

L'output mostrerebbe i messaggi con solo un minuto di osservazione che l'operazione 'Async completata' richiede tempo e viene stampata dopo il messaggio 'Fine':

Fase di controllo di Node.js

Dopo l'esecuzione della fase di poll vengono eseguiti i callback nella fase di controllo. Se uno script di codice viene pianificato utilizzando la funzione setImmediate() e la funzione di poll è libera, il ciclo di eventi funziona passando direttamente alla fase di controllo invece di rimanere inattivo. La funzione setImmediate() è un timer unico che opera durante le diverse fasi del ciclo degli eventi.

L'API libuv viene utilizzata per pianificare le esecuzioni di callback dopo che l'esecuzione della fase di poll è stata completata. Durante l'esecuzione del codice, l'event loop entra nella fase di poll in cui attende le richieste di connessione in entrata. In un altro caso, se la richiamata viene pianificata utilizzando la funzione setImmediate() e la fase di poll viene terminata senza alcuna attività, si passerà alla fase di controllo invece di attendere. Considera l'esempio seguente per capire:

// applicazione.js

consolle. tronco d'albero ( 'Inizio' ) ;

setImmediato ( ( ) => {

consolle. tronco d'albero ( 'Richiamata immediata' ) ;

} ) ;

consolle. tronco d'albero ( 'FINE' ) ;

In questo codice vengono registrati tre messaggi sul terminale. La funzione setImmediate() invia infine una richiamata per registrare il messaggio ' Richiamata immediata ' al terminale.

Produzione

L'output del codice precedente verrà visualizzato nella seguente sequenza:

Node.js chiude i callback

Node.js utilizza questa fase di chiusura per eseguire callback per chiudere gli eventi e terminare un'iterazione del ciclo di eventi. Dopo che la connessione è stata chiusa, il loop degli eventi gestisce gli eventi di chiusura in questa fase. In questa fase del ciclo degli eventi, 'nextTick()' e microtask vengono generati ed elaborati in modo simile alle altre fasi.

La funzione process.exit viene utilizzata per terminare il ciclo degli eventi in qualsiasi istante. Il ciclo di eventi ignorerà qualsiasi operazione asincrona in sospeso e il processo Node.js terminerà.

Un semplice esempio da considerare è:

// applicazione.js
cost netto = richiedere ( 'netto' ) ;
cost server = netto. creareServer ( ( PRESA ) => {
PRESA. SU ( 'vicino' , ( ) => {
consolle. tronco d'albero ( 'Presa chiusa' ) ;
} ) ;
PRESA. SU ( 'dati' , ( dati ) => {
consolle. tronco d'albero ( 'Dati ricevuti:' , dati. accordare ( ) ) ;
} ) ;
} ) ;
server. SU ( 'vicino' , ( ) => {
consolle. tronco d'albero ( 'Server chiuso' ) ;
} ) ;
cost porta = 3000 ;
server. Ascoltare ( porta, ( ) => {
consolle. tronco d'albero ( `Server in ascolto sulla porta $ { porta } ` ) ;
} ) ;
setTimeout ( ( ) => {
consolle. tronco d'albero ( 'Chiusura del server dopo 10 secondi' ) ;
server. vicino ( ) ;
processi. Uscita ( ) ;
} , 10000 ) ;

In questo codice:

  • const netto = require('netto') ' importa il modulo di rete necessario per gestire un server TCP e ' const server = net.createServer((socket) => { ' crea una nuova istanza del server TCP.
  • socket.on('chiudi', () => {… } ” ascolta la “chiusura” su tutte le prese. Quando la connessione del socket viene chiusa, sul terminale viene registrato il messaggio “Socket chiuso”.
  • socket.on('dati', (dati) => {} ' controlla i dati in entrata da tutti i singoli socket e li stampa utilizzando la funzione '.toString()'.
  • server.on('chiudi', () => {...} ' controlla l'evento 'chiusura' sul server stesso e quando la connessione al server viene chiusa registra il messaggio 'Server chiuso' sul terminale.
  • server.listen(porta, () => {...} ” ascolta le connessioni in entrata sulla porta.
  • setTimeout(() => {...} ” imposta un timer di 10 ms per chiudere il server.

Questo conclude la discussione sulle varie fasi del ciclo di eventi in Node.js. Prima di saltare a una conclusione discutiamo di un'ultima cosa, ovvero come uscire dal ciclo degli eventi in Node.js.

Uscire dal ciclo degli eventi in Node.js

Il ciclo di eventi è in fase di esecuzione finché sono presenti alcune attività in tutte le code delle fasi del ciclo di eventi. Il ciclo degli eventi termina dopo l'emissione della fase di uscita e la richiamata del listener di uscita ritorna se non sono presenti più attività nelle code.

Il modo esplicito per terminare un ciclo di eventi è utilizzare il metodo '.exit'. I processi attivi di Node.js termineranno immediatamente non appena verrà chiamata la funzione process.exit. Tutti gli eventi programmati e in sospeso verranno eliminati:

processi. SU ( 'Uscita' , ( codice ) => {

consolle. tronco d'albero ( `Uscire con il codice di uscita : $ { codice } ` ) ;

} ) ;

processi. Uscita ( 1 ) ;

Gli utenti possono ascoltare la funzione .exit. È da notare che la funzione “.exit” deve essere sincrona poiché il programma Node.js uscirà non appena ascolterà questo evento.

Questo conclude la discussione sul ciclo degli eventi. Un articolo approfondito che ha trattato tutti i concetti, le fasi e gli esempi relativi all'event loop.

Conclusione

Prima di comprendere il ciclo degli eventi, una panoramica dei concetti sincrono e asincrono può aiutare a comprendere il flusso del codice nel ciclo degli eventi. L'esecuzione sincrona significa l'esecuzione passo-passo mentre l'esecuzione asincrona significa interrompere alcuni passaggi senza attendere il loro completamento. Il funzionamento del ciclo di eventi insieme a tutte le fasi insieme ad esempi adeguati sono discussi nell'articolo.