2012-04-07 6 views
5

Ich habe und Anwendung in NodeJS mit Express geschrieben und versuche, EventEmitter zu verwenden, um eine Art Plugin-Architektur mit Plugins Hooking in den Hauptcode durch Anhören emittierter Ereignisse zu erstellen.NodeJS warten auf Callback zum Beenden auf Ereignis emittieren

Mein Problem kommt, wenn eine Plugin-Funktion eine asynchrone Anfrage macht (um in diesem Fall Daten von mongo zu erhalten), dies bewirkt, dass der Plugin-Code endet und die Kontrolle an den ursprünglichen Emitter zurückgibt, der dann die Ausführung vor der asynchronen Anfrage beendet im Plugin-Code endet.

ZB:

Haupt 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! 
} 

Mein Grund, eine Callback-Funktion zurück zum Hauptcode von den 'getFromMongo' Funktion zur Vermeidung ist, dass es kann 0 oder viele Plugins sein, die dem Ereignis zuhören. Im Idealfall einen Weg Ich will warten, bis die DB Sachen, bevor die Steuerung an die Haupt App

Vielen Dank Rückkehr zu beenden

Antwort

3

die EventEmitter für Plugin/Middleware-Management-Verwendung ist nicht ideal, weil Sie nicht sicherstellen können, dass die Zuhörer sequentiell ausgeführt, wenn sie asynchronen Code haben. Dies ist insbesondere ein Problem, wenn diese Listener miteinander oder mit den gleichen Daten interagieren.

Deshalb werden z. B. connect/express-Middleware-Funktionen in einem Array gespeichert und nacheinander ausgeführt, anstatt einen EventEmitter zu verwenden; Sie müssen jeweils ein next() aufrufen; Funktion, wenn sie ihre Aufgabe erledigt haben.

3

Sie können asynchrone Aufrufe nicht mit synchronem Verhalten mischen. Wenn Sie bei Event Emitter bleiben (was für Sie vielleicht nicht ideal ist, wie Klovadis darauf hinwies), müssen Sie Ihr Plug-in ein Event ausgeben lassen, das eine Funktion in der Haupt-App auslöst, die den von Ihnen gewünschten Code enthält warten, um auszuführen. Außerdem müssen Sie alle Plug-in-Aufrufe protokollieren, die Sie für Ereignisaufrufe erstellt haben, damit Ihr Hauptcode erst ausgeführt wird, wenn alle Aufrufe von MongoDB die Rückrufe beendet haben.

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

habe es selbst nicht ausprobiert, aber Sie eine Immobilie in dem Veranstaltungsdatenobjekt als ein Array von Funktionen durch den Code auszuführen, verwenden könnten, die das Ereignis emittieren:

Hörer

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) } 
    } 
}); 

Emitter

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

Hat die gleiche Art von Entscheidung über einige Ereignisse zu machen, die Manchmal muss ich darauf warten, bevor ich die Antwort an den Client zurücksende und manchmal nicht (wenn ich nicht in einem HTTP-Anfrage-Kontext bin).

Der einfachste Weg für mich war, einen Rückruf als letztes Argument des Ereignisses hinzuzufügen.

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

Für den Fall prüfen, ob es einen Rückruf:

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

Ich weiß, dass das Mischen Ereignis und Rückrufe chaotisch sein kann, sondern dass die Arbeit in Ordnung für das, was ich brauche. Die andere Lösung, die ich sehe, ist die von @redben vorgeschlagene: Fügen Sie am Ende des Ereignisses eine Emit-Funktion hinzu. Das Problem, wenn Sie in einem HTTP-Kontext sind, dass Sie eindeutige Schlüssel benötigen, damit Ihre Ereignisse nicht durcheinander geraten, wenn sie verschiedene Dinge pro Benutzer ausführen.