在 NodeJS 中发送 HTTP 响应之前等待事件发生?
Wait for an event to happen before sending HTTP response in NodeJS?
我正在寻找在发送 HTTP 响应之前等待事件发生的解决方案。
用例
- 我的想法是在我的一条路线中调用一个函数:
zwave.connect("/dev/ttyACM5");
这个函数 return 立即。
- 但是存在 2 个事件通知连接设备是成功还是失败:
zwave.on('driver ready', function(){...});
zwave.on('driver failed', function(){...});
- 在我的路由中,我想知道设备在发送 HTTP 响应之前是连接成功还是失败。
我的"solution"
- 当事件发生时,我将事件保存在数据库中:
zwave.on('driver ready', function(){
//In the database, save the fact the event happened, here it's event "CONNECTED"
});
- 在我的路线中,执行连接功能并等待事件发生
出现在数据库中:
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 怎么样?
我已经将 asynchronous and control-flow 标签添加到您的问题中,因为它的核心就是您要问的问题。 (顺便说一句,如果您不使用 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 Promise
s 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);
}
});
});
}
我正在寻找在发送 HTTP 响应之前等待事件发生的解决方案。
用例
- 我的想法是在我的一条路线中调用一个函数:
zwave.connect("/dev/ttyACM5");
这个函数 return 立即。 - 但是存在 2 个事件通知连接设备是成功还是失败:
zwave.on('driver ready', function(){...});
zwave.on('driver failed', function(){...});
- 在我的路由中,我想知道设备在发送 HTTP 响应之前是连接成功还是失败。
我的"solution"
- 当事件发生时,我将事件保存在数据库中:
zwave.on('driver ready', function(){
//In the database, save the fact the event happened, here it's event "CONNECTED"
});
- 在我的路线中,执行连接功能并等待事件发生 出现在数据库中:
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 怎么样?
我已经将 asynchronous and control-flow 标签添加到您的问题中,因为它的核心就是您要问的问题。 (顺便说一句,如果您不使用 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 Promise
s or the Reactive Extensions for JavaScript (a.k.a RxJS)。
示例使用 Promise
来自 MDN:
The
Promise
object is used for asynchronous computations. APromise
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);
}
});
});
}