Come utilizzare gli eventi in Node.js nel modo giusto

Prima che la programmazione guidata dagli eventi diventasse popolare, il modo standard di comunicare tra le diverse parti di un'applicazione era piuttosto semplice: un componente che voleva inviare un messaggio a un altro invocava esplicitamente un metodo su quel componente. Ma il codice basato sugli eventi è scritto per reagire anziché essere chiamato.

I vantaggi di Eventing

Questo approccio fa sì che i nostri componenti siano molto più disaccoppiati. Mentre continuiamo a scrivere un'applicazione, identifichiamo gli eventi lungo la strada. Li licenziamo al momento giusto e alleghiamo uno o più ascoltatori di eventi. L'estensione della funzionalità diventa molto più semplice. Possiamo aggiungere più ascoltatori a un determinato evento. Non stiamo manomettendo gli ascoltatori esistenti o la parte dell'applicazione da cui è stato generato l'evento. Ciò di cui stiamo parlando è il modello Observer.

https://www.dofactory.com/javascript/observer-design-pattern

Progettare un'architettura orientata agli eventi

Identificare gli eventi è piuttosto importante. Non vogliamo finire per dover rimuovere / sostituire eventi esistenti dal sistema. Questo potrebbe costringerci ad eliminare / modificare qualsiasi numero di ascoltatori che erano collegati all'evento. Il principio generale che uso è quello di considerare di lanciare un evento solo quando un'unità di business logic termina l'esecuzione.

Quindi supponi di voler inviare un sacco di email diverse dopo la registrazione di un utente. Ora, il processo di registrazione stesso potrebbe comportare molti passaggi complicati e query. Ma dal punto di vista commerciale, è un passo. E ciascuna delle e-mail da inviare sono anche singole fasi. Quindi avrebbe senso sparare un evento non appena termina la registrazione. Abbiamo più ascoltatori ad esso collegati, ognuno responsabile per l'invio di un tipo di email.

L'architettura asincrona, guidata dagli eventi, di Node ha alcuni tipi di oggetti chiamati "emettitori". Emettono eventi con nome che fanno invocare funzioni chiamate "ascoltatori". Tutti gli oggetti che emettono eventi sono istanze della classe EventEmitter. Usandolo, possiamo creare i nostri eventi:

Un esempio

Usiamo il modulo eventi integrato (che ti incoraggio a controllare in dettaglio) per accedere a EventEmitter.

Questa è la parte dell'applicazione in cui il nostro server riceve una richiesta HTTP, salva un nuovo utente ed emette un evento:

E un modulo separato in cui alleghiamo un ascoltatore:

È buona norma separare la politica dall'attuazione. In questo caso, la politica indica quali ascoltatori sono abbonati a quali eventi. Implementazione significa gli stessi ascoltatori.

Questa separazione consente di riutilizzare anche l'ascoltatore. Può essere allegato ad altri eventi che inviano lo stesso messaggio (un oggetto utente). È anche importante ricordare che quando più listener sono associati a un singolo evento, verranno eseguiti in modo sincrono e nell'ordine in cui sono stati collegati. Quindi someOtherListener verrà eseguito dopo che sendEmailOnRegistration termina l'esecuzione.

Tuttavia, se desideri che i tuoi ascoltatori vengano eseguiti in modo asincrono, puoi semplicemente avvolgere le loro implementazioni con setImmediate in questo modo:

Mantieni i tuoi ascoltatori puliti

Attenersi al principio di responsabilità singola quando si scrivono ascoltatori. Un ascoltatore dovrebbe fare solo una cosa e farlo bene. Evitare, ad esempio, di scrivere troppi condizionali all'interno di un ascoltatore che decidono cosa fare in base ai dati (messaggio) trasmessi dall'evento. In questo caso sarebbe molto più appropriato utilizzare eventi diversi:

Staccare esplicitamente gli ascoltatori quando necessario

Nell'esempio precedente, i nostri ascoltatori erano funzioni totalmente indipendenti. Ma nei casi in cui un ascoltatore è associato a un oggetto (è un metodo), deve essere staccato manualmente dagli eventi a cui si è iscritto. Altrimenti, l'oggetto non verrà mai raccolto in modo errato poiché una parte dell'oggetto (l'ascoltatore) continuerà a essere referenziata da un oggetto esterno (l'emettitore). Quindi la possibilità di una perdita di memoria.

Ad esempio, se stiamo costruendo un'applicazione di chat e vogliamo la responsabilità di mostrare una notifica quando arriva un nuovo messaggio in una chat room a cui un utente si è connesso dovrebbe trovarsi all'interno dell'oggetto utente stesso, potremmo farlo:

Quando l'utente chiude la sua scheda o perde la connessione a Internet per un po ', naturalmente, potremmo voler attivare una richiamata sul lato server che avvisa gli altri utenti che uno di loro è appena andato offline. A questo punto, ovviamente, non ha senso invocare displayNewMessageNotification per l'utente offline. Continuerà a essere chiamato per i nuovi messaggi a meno che non lo rimuoviamo esplicitamente. Se non lo facciamo, a parte la chiamata non necessaria, anche l'oggetto utente rimarrà nella memoria indefinitamente. Quindi assicurati di chiamare disconnectFromChatroom nel callback sul lato server che viene eseguito ogni volta che un utente passa offline.

diffidare

L'accoppiamento lento nelle architetture guidate dagli eventi può anche portare a una maggiore complessità se non stiamo attenti. Può essere difficile tenere traccia delle dipendenze nel nostro sistema. La nostra applicazione diventerà particolarmente soggetta a questo problema se iniziamo a emettere eventi dall'interno degli ascoltatori. Ciò potrebbe innescare catene di eventi imprevisti.