2012-04-07 13 views
5

Ho e applicazione scritta in NodeJS con Express e sto tentando di usare EventEmitter per creare un tipo di architettura plugin con plugin che si agganciano al codice principale ascoltando gli eventi emessi.NodeJS attendi che la richiamata finisca su evento emetta

Il mio problema arriva quando una funzione di plugin fa una richiesta asincrona (per ottenere dati da mongo in questo caso) questo fa sì che il codice del plugin finisca e restituisca il controllo all'emettitore originale che completerà l'esecuzione, prima della richiesta asincrona nel codice del plugin finisce.

Esempio:

principale App:

// We want to modify the request object in the plugin 
self.emit('plugin-listener', request); 

Plugin:

// Plugin function listening to 'plugin-listener', 'request' is an arg 
console.log(request); 

// Call to DB (async) 
this.getFromMongo(some_data, function(response){ 
    // this may not get called until the plugin function has finished! 
} 

La ragione per evitare una funzione di callback di nuovo al codice principale dalla funzione 'getFromMongo' è che ci potrebbe essere 0 o molti plugin che ascoltano l'evento. Idealmente voglio un modo per attendere la roba DB per terminare prima di restituire il controllo l'applicazione principale

Molte grazie

risposta

3

Utilizzando l'EventEmitter per plugin/gestione middleware non è l'ideale, perché non si può garantire che gli ascoltatori sono eseguito sequenzialmente, se hanno codice asincrono. Questo è particolarmente un problema quando questi ascoltatori interagiscono tra loro o con gli stessi dati.

Ecco perché le funzioni middleware connect/express sono memorizzate in un array ed eseguite una dopo l'altra, invece di utilizzare EventEmitter; Ognuno di loro deve chiamare un next(); funzione quando hanno finito di svolgere il loro compito.

3

Non è possibile combinare chiamate asincrone con comportamento sincrono. Se vuoi attaccare con un emettitore di eventi (che potrebbe non essere l'ideale per te come ha sottolineato Klovadis), devi avere il tuo plugin che emetta un evento che attiva una funzione nell'app principale che contiene il codice che vuoi "aspettare" per eseguire. Dovresti inoltre tenere traccia di tutte le chiamate di plug-in effettuate che stai aspettando per le chiamate di eventi, in modo che il tuo codice principale non venga eseguito fino a quando tutte le chiamate al plug-in non abbiano terminato i loro callback MongoDB.

var callList = ['pluginArgs1', 'pluginArgs2', 'pluginArgs3']; 
for (var i = 0; i < callList.length; i++){ 
    self.emit('plugin-listener', callList[i], i); 
} 

self.on('plugin-callback', function(i){ 
    callList.splice(i, 1); 
    if (callList.length < 1){ 
    //we're done, do something 
    } 
}); 
0

non ho provato io stesso, ma è possibile utilizzare una proprietà in oggetto dati dell'evento come una serie di funzioni per l'esecuzione da parte del codice che ha emesso l'evento:

ascoltatori

foo.on('your-event', function(data) { 
    console.log(data); 
    // Then add the asynchronous code to a callbacks array 
    // in the event data object 
    data.callbacks.push(function(next) { 
    getFromMongo(some_data, function(err, result) { next(err) } 
    } 
}); 

emettitore

self.emit('your-event', data); 
// listeners have modified data object, 
// some might have added callback to data.callbacks 
// (suppose you use async) 
async.series(data.callbacks); 
1

avuto lo stesso tipo di decisione da prendere su alcuni eventi che A volte ho bisogno di aspettare prima di restituire la risposta al client e talvolta no (quando non in un contesto di richiesta HTTP).

Il modo più semplice per me era aggiungere una richiamata come ultimo argomento dell'evento.

Stuff.emit('do_some_stuff', data, data2, callback); 

Nel controllo evento se c'è un callback:

Stuff.on('do_some_stuff', function(data, data2, callback) { 
    // stuff to do 
    // ... 
    if (typeof callback === "function") return callback(err, result); 
}); 

So che la miscelazione degli eventi e callback possono essere disordinato, ma che funzionano bene per quello che mi serve. L'altra soluzione che vedo è quella proposta da @redben: aggiungi una funzione emit alla fine dell'evento. Il problema quando ci si trova in un contesto HTTP è che sono necessarie chiavi univoche in modo che gli eventi non si rovinino se fanno cose diverse per utente.

Problemi correlati