Menu

EventEmitter Node.js: on, emit, once ed eventi custom

EventEmitter, del modulo events di Node, permette a un oggetto di annunciare eventi con un nome tramite emit e ad altro codice di reagire con on o once. I listener vengono eseguiti in modo sincrono, nell'ordine in cui sono stati aggiunti.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

EventEmitter è la classe del modulo events di Node che permette a un oggetto di annunciare eventi con un nome. Registri un listener con emitter.on('name', fn) e lo fai scattare con emitter.emit('name', ...args); ogni listener con quel nome viene eseguito, nell'ordine in cui è stato aggiunto, con gli argomenti che hai passato. Server HTTP, stream e process sono tutti EventEmitter, quindi gli stessi pochi metodi funzionano ovunque in Node.

Entrambi i listener vengono eseguiti tra before emit e after emit: emit li chiama in modo sincrono e ritorna solo quando hanno finito. Scambia le due chiamate a shop.on e si scambiano anche le due righe centrali.

on, once e off

on tiene un listener finché non lo rimuovi, once lo rimuove dopo la prima chiamata e off (uguale a removeListener) ne rimuove uno. Per rimuovere un listener ti serve la stessa funzione che hai aggiunto, quindi dalle un nome invece di passare una arrow function direttamente.

first open ever stampa una sola volta, dopo Ada. Una volta rimosso logOpen non c'è più nessuno in ascolto, quindi l'ultimo emit restituisce false; door.removeAllListeners('open') avrebbe tolto entrambi i listener con una sola chiamata.

MetodoCosa fa
on(name, fn), addListenerChiama fn a ogni emit
once(name, fn)Chiama fn solo al prossimo emit
prependListener(name, fn)Come on, ma gira prima dei listener esistenti
off(name, fn), removeListenerRimuove quella funzione
emit(name, ...args)Chiama i listener, restituisce true se ce n'erano
listenerCount(name)Quanti listener ha l'evento

L'evento error

'error' è speciale. Se lo emetti e nessuno è in ascolto, emit lancia l'errore, e in un programma vero un'eccezione non gestita fa crashare il processo. Aggiungi sempre un listener 'error' agli emitter che possono fallire, come stream e server.

Senza listener, emit lancia l'errore e il catch lo raccoglie; con il listener aggiunto, lo stesso emit si limita a chiamarlo e il programma va avanti. Qualsiasi altro nome di evento senza listener viene ignorato in silenzio.

Estendere EventEmitter nella tua classe

Il modo più comune di usarlo è far estendere EventEmitter alla tua classe, chiamare super() nel costruttore e this.emit quando succede qualcosa. Chi usa la classe si iscrive poi al tuo oggetto come a qualsiasi oggetto di Node.

Stampa 25%, 50%, 75%, 100% e poi photo.png uploaded. Sposta up.start() sopra le due righe up.on e non stampa niente: gli eventi partono prima che qualcuno sia in ascolto.

La stessa trappola vale quando emetti da un costruttore. Le librerie che devono emettere subito avvolgono la chiamata in process.nextTick, che gira dopo le righe on del chiamante (vedi la pagina sull'event loop).

Aspettare un evento con events.once

events.once(emitter, name) restituisce una promise che si risolve con gli argomenti dell'evento, in un array, la prossima volta che viene emesso. Trasforma un evento che capita una volta in qualcosa su cui puoi fare await, e la promise viene rifiutata se l'emitter emette prima 'error'.

L'await si ferma finché il timer non emette ready, poi l'array viene destrutturato nei due argomenti. Per un evento che scatta molte volte, events.on(emitter, name) fornisce un iteratore asincrono da scorrere con for await.

Errori comuni

Aggiungere un listener dentro una funzione che gira spesso. Chiamare on in ogni request handler accumula listener e memoria. Aggiungilo una volta sola, oppure usa once.

Rimuovere una funzione scritta in linea. off('data', () => ...) non rimuove niente, perché quella arrow function è una funzione nuova. Tieni un riferimento alla funzione che hai aggiunto.

Aspettarsi che i listener girino più tardi. emit è sincrono, quindi un listener lento blocca il codice che ha emesso. Segnare il listener come async non aiuta, perché il suo codice gira comunque sullo stesso thread fino al primo await. Dividi il lavoro pesante in pezzi con setImmediate in mezzo, oppure spostalo in un worker thread.

Domande frequenti

EventEmitter è asincrono?

No. emit chiama ogni listener subito, uno dopo l'altro, e ritorna quando l'ultimo ha finito. Il codice dentro un listener può avviare lavoro asincrono, ma il listener in sé viene chiamato in modo sincrono.

Cosa significa il MaxListenersExceededWarning?

Node avvisa quando per un evento vengono aggiunti più di 10 listener, perché spesso significa che un listener viene aggiunto a ogni richiesta e mai rimosso. Rimuovi i listener in più, oppure chiama emitter.setMaxListeners(n) se te ne servono davvero di più.

Cosa restituisce emit?

true se l'evento aveva almeno un listener, false se nessuno era in ascolto. Non restituisce quello che restituiscono i listener.

Quali oggetti di Node.js sono EventEmitter?

La maggior parte degli oggetti core di Node: server HTTP, richieste e risposte, stream, socket, process e le interfacce di readline. Per questo condividono tutti on, once e off.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

Inizia