在 NodeJS 中发送 HTTP 响应之前等待事件发生?

Wait for an event to happen before sending HTTP response in NodeJS?

我正在寻找在发送 HTTP 响应之前等待事件发生的解决方案。

用例

  1. 我的想法是在我的一条路线中调用一个函数:zwave.connect("/dev/ttyACM5"); 这个函数 return 立即。
  2. 但是存在 2 个事件通知连接设备是成功还是失败:
zwave.on('driver ready', function(){...});
zwave.on('driver failed', function(){...});
  1. 在我的路由中,我想知道设备在发送 HTTP 响应之前是连接成功还是失败。

我的"solution"

  1. 当事件发生时,我将事件保存在数据库中:
zwave.on('driver ready', function(){
    //In the database, save the fact the event happened, here it's event "CONNECTED"
});
  1. 在我的路线中,执行连接功能并等待事件发生 出现在数据库中:
router.get('/', function(request, response, next) {     
    zwave.connect("/dev/ttyACM5");
    waitForEvent("CONNECTED", 5, null, function(){
        response.redirect(/connected);
    });
});

// The function use to wait for the event
waitForEvent: function(eventType, nbCallMax, nbCall, callback){
    if(nbCall == null) nbCall = 1;
    if(nbCallMax == null) nbCallMax = 1;

    // Looking for event to happen (return true if event happened, false otherwise
    event = findEventInDataBase(eventType);

    if(event){
        waitForEvent(eventType, nbCallMax, nbCall, callback);
    }else{
        setTimeout(waitForEvent(eventType, callback, nbCallMax, (nbCall+1)), 1500);
    }
}

我认为这不是一个好的做法,因为它会在数据库上迭代调用。 那么你的 opinions/suggestions 怎么样?

我已经将 and 标签添加到您的问题中,因为它的核心就是您要问的问题。 (顺便说一句,如果您不使用 ES6,您应该能够将下面的代码转换回 ES5。)

TL;DR

JavaScript 中有很多方法可以处理异步控制流(另请参阅:What is the best control flow module for node.js?). You are looking for a structured way to handle it—likely Promises or the Reactive Extensions for JavaScript (a.k.a RxJS)

示例使用 Promise

来自 MDN:

The Promise object is used for asynchronous computations. A Promise represents a value which may be available now, or in the future, or never.

在您的案例中,异步计算是描述连接到设备的成功或失败的布尔值的计算。为此,您可以将对 connect 的调用包装在 Promise 对象中,如下所示:

const p = new Promise((resolve) => {
    // This assumes that the events are mutually exclusive
    zwave.connect('/dev/ttyACM5');
    zwave.on('driver ready', () => resolve(true));
    zwave.on('driver failed', () => resolve(false));
});

一旦你有一个代表连接状态的Promise,你就可以将函数附加到它的"future"值:

// Inside your route file
const p = /* ... */;
router.get('/', function(request, response, next) {
    p.then(successful => {
        if (successful) {
            response.redirect('/connected');
        }
        else {
            response.redirect('/failure');
        }
    });
});

您可以了解有关 Promises 的更多信息 on MDN, or by reading one of many other resources on the topic (e.g. You're Missing the Point of Promises)。

你试过这个吗?看起来,你的zwave可能已经实现了一个EventEmmiter,你只需要给它附加一个监听器

router.get('/', function(request, response, next) {     
    zwave.connect("/dev/ttyACM5");
    zwave.once('driver ready', function(){
        response.redirect(/connected);
    });
});

还有一个 npm sync 模块。用于同步执行查询的过程。

当你想运行以同步方式并行查询时,节点限制这样做,因为它从不等待响应。同步模块非常适合这种解决方案。

示例代码

   /*require sync module*/
var Sync = require('sync');
    app.get('/',function(req,res,next){
      story.find().exec(function(err,data){
        var sync_function_data = find_user.sync(null, {name: "sanjeev"});
          res.send({story:data,user:sync_function_data});
        });
    });


    /*****sync function defined here *******/
    function find_user(req_json, callback) {
        process.nextTick(function () {

            users.find(req_json,function (err,data)
            {
                if (!err) {
                    callback(null, data);
                } else {
                    callback(null, err);
                }
            });
        });
    }

参考link:https://www.npmjs.com/package/sync