【问题标题】:NodeJS wait for callback to finish on event emitNodeJS 等待回调在事件发出时完成
【发布时间】:2012-04-20 15:18:00
【问题描述】:

我有一个用 NodeJS 和 Express 编写的应用程序,我正在尝试使用 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 个或多个插件正在监听该事件。理想情况下,我希望通过某种方式等待数据库内容完成,然后再将控制权返回给主应用

非常感谢

【问题讨论】:

    标签: events node.js express eventemitter


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      对某些事件做出相同的决定,有时我需要等待才能将响应返回给客户端,有时则不需要(当不在 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 上下文中的问题是您需要唯一的键,这样如果每个用户执行不同的操作,您的事件就不会混乱。

      【讨论】:

      • 如果您有多个事件监听器,此方法将不起作用
      • On 方法是从你的听众那里发出一个新的甚至等待它
      【解决方案3】:

      自己没有尝试过,但您可以使用事件数据对象中的属性作为函数数组,由发出事件的代码执行:

      听众

      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】:

        您不能将异步调用与同步行为混为一谈。如果您要坚持使用事件发射器(正如 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
          }
        });
        

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            猜你喜欢
            • 2019-11-19
            • 2016-03-09
            • 2016-02-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-04-28
            • 2017-03-27
            • 1970-01-01
            相关资源
            最近更新 更多