我用Node编写了NodeJS并尝试使用EventEmitter创建一种插件架构,插件通过监听发出的事件挂钩到主代码中。
当插件函数发出异步请求(在这种情况下从mongo获取数据)时出现问题,这导致插件代码完成并将控制权返回到原始发射器,然后在异步请求之前完成执行插件代码完成。
E.g:
主要应用程序:
// We want to modify the request object in the plugin
self.emit('plugin-listener', request);
插件:
// 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!
}
我从'getFromMongo'函数回避主代码的回调函数的原因是可能有0个或多个插件监听事件。理想情况下,我想要一些方法等待数据库的东西完成,然后再将控制权返回给主应用程序
非常感谢
答案 0 :(得分:3)
使用EventEmitter进行插件/中间件管理并不理想,因为如果侦听器具有异步代码,则无法确保侦听器按顺序执行。当这些侦听器彼此交互或相同数据时,这尤其是一个问题。
这就是为什么连接/表达中间件函数存储在一个数组中并一个接一个地执行,而不是使用EventEmitter;他们每个人都需要调用next();在他们完成任务时发挥作用。
答案 1 :(得分:3)
您不能将异步调用与同步行为混合在一起。如果您要坚持使用事件发射器(这可能不像Klovadis指出的那样理想),您需要让您的插件发出一个事件,触发主应用程序中包含您想要的代码的功能'等待'执行。您还必须依次跟踪您正在等待事件调用的所有插件调用,以便在所有插件调用完成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
}
});
答案 2 :(得分:1)
在将响应返回给客户端之前有时需要等待的某些事件做出同样的决定,有时候没有(当不在HTTP请求上下文中时)。
对我来说最简单的方法是添加一个回调作为事件的最后一个参数。
Stuff.emit('do_some_stuff', data, data2, callback);
在事件中检查是否有回调:
Stuff.on('do_some_stuff', function(data, data2, callback) {
// stuff to do
// ...
if (typeof callback === "function") return callback(err, result);
});
我知道混合事件和回调可能会很混乱,但这对我需要的工作很好。 我看到的另一个解决方案是@redben提出的解决方案:在事件结束时添加一个emit函数。在HTTP上下文中的问题是您需要唯一的密钥,因此如果每个用户执行不同的操作,您的事件就不会搞乱。
答案 3 :(得分:0)
我自己没有尝试过,但您可以将事件数据对象中的属性用作由发出事件的代码执行的函数数组:
监听
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) }
}
});
发射极
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);
答案 4 :(得分:0)
这似乎很危险,但是我还是必须这样做...
const ee = new EventEmitter();
if (ee.listeners("async-event").length > 0) {
await new Promise((resolve) => {
ee.emit("async-event", data1, data2, resolve);
});
}
否则,只需反复发出事件即可。