12

我有一个用 Express 用 NodeJS 编写的应用程序,并且正在尝试使用 EventEmitter 创建一种插件架构,其中插件通过监听发出的事件连接到主代码中。

当插件函数发出异步请求(在这种情况下从 mongo 获取数据)时,我的问题就出现了,这会导致插件代码完成并将控制权返回给原始发射器,然后在插件代码中的异步请求之前完成执行完成。

例如:

主应用:

// 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 个或多个插件正在监听该事件。理想情况下,我想要某种方式来等待数据库内容完成,然后再将控制权返回给主应用程序

非常感谢

4

5 回答 5

5

使用 EventEmitter 进行插件/中间件管理并不理想,因为如果侦听器具有异步代码,则无法确保它们按顺序执行。当这些侦听器与彼此或相同数据交互时,这尤其是一个问题。

这就是为什么 ie connect/express 中间件函数存储在一个数组中并一个接一个地执行,而不是使用 EventEmitter;他们每个人都需要调用一个 next(); 当他们完成任务时发挥作用。

于 2012-04-07T09:21:09.653 回答
4

您不能将异步调用与同步行为混为一谈。如果您要坚持使用事件发射器(正如 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
  }
});
于 2012-04-09T17:17:09.960 回答
3

在将响应返回给客户端之前有时需要等待一些事件,有时不需要(当不在 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 提出的解决方案:在事件结束时添加一个发出函数。在 HTTP 上下文中的问题是您需要唯一的键,这样如果每个用户执行不同的操作,您的事件就不会混乱。

于 2014-01-20T09:58:16.360 回答
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);
于 2013-09-12T00:10:18.833 回答
0

这似乎很危险,但无论如何我必须这样做......

const ee = new EventEmitter();
if (ee.listeners("async-event").length > 0) {
  await new Promise((resolve) => {
    ee.emit("async-event", data1, data2, resolve);
  });
}

否则,只需来回发出事件。

于 2019-09-05T18:34:06.863 回答