eventEmitter回调后Node.js http请求关闭响应

时间:2019-08-29 09:21:14

标签: node.js http callback es6-modules eventemitter

我有一个在Raspberry Pi上运行的node.js ES6项目,该项目带有一个主文件和一些导入的ES6子模块(示例中为http和一个温度传感器)。

当服务器收到请求时,我想调用一个异步父函数(我可以使用EventEmitter做到这一点),并在页面上回调打印温度值。

我可以这样做吗? 问题是响应没有关闭(因此客户端页面永远不会结束),如果我重复调用,它会告诉我服务器:port已经很忙。

简短摘要

...
self.webserver.on('request', async (req, res) => {
  ...
  self.emit('remoteCall', path, JSON.stringify(queryData)); // call parent
  self.on('callback',(data)=>{ // <-- HERE callback from index.js with temperature data collected
   res.write(data);
   res.end(); // <-- I want to wait for temperature data and then end server response
  });
});
...

完整示例

/**
 * ./app/services/server/server.js Server module
 */
const http = require('http');
const url = require('url');
const EventEmitter = require( 'events' );

class Server extends EventEmitter {
  constructor(){
    super();
    this.webserver = http.createServer();
    this.remoteServer = 'https://example.com/';
  }
  startServer(){
    const self = this;
    self.webserver.on('request', async (req, res) => {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      const q = url.parse(req.url, true);
      if (q.pathname === '/favicon.ico') {
        res.writeHead(200, {'Content-Type': 'image/x-icon'} );
        res.end();
        return;
      }
      const path = q.pathname;
      const queryData = q.query;
      res.write(path);
      res.write(JSON.stringify(queryData));

      self.emit('remoteCall', path, JSON.stringify(queryData)); // <-- send to index.js so it can execute temperature read
      self.on('callback',(data)=>{ // <-- HERE callback from index.js with temperature data collected
        console.log(data);
        res.write(data);
        res.end(); // <-- I want to wait for temperature data and then end server response
      }); 
    });
  }
  listen(){
    this.webserver.listen(8080);
    return
  }
}
export default Server;
/**
 * ./app/probes/tempereture/tempereture.js Temperature module
 */
const sensor = require('ds18x20');

class Temperature {
  constructor(id) {
    this.id = id;
  }
  async getTemperature() {
    const self = this;
    return new Promise(resolve => {
      sensor.get(self.id, function (err, tempObj) {
        if (err) { throw err; }
        resolve({id: self.id, value: tempObj});
      });
    });
  }
}
export default Temperature;
/**
 * ./app/evoirement/pot/pot.js Temperature object parent
 */
import Temperature from '../../probes/temperature/temperature';

class Pot {
  constructor({potID, waterTemperatureProbeID}) {
    this.id = potID;
    this.waterTemperature = new Temperature(waterTemperatureProbeID);
  }
}
export default Pot;
/**
 * index.js Main file
 */

import Pot from './app/envoirement/pot/pot';
import Server from './app/services/server/server';

class Main {
  constructor(){
    this.server = new Server();
    this.appSetup();
  }
  appSetup(){
    const self = this;
    this.pot1 = new Pot({
      potID: 'pot1',
      waterTemperatureProbeID: '28-0114502296aa'
    });
    this.pot2 = new Pot({
      potID: 'pot2',
      waterTemperatureProbeID: '28-0114504ea1aa'
    });

    this.server.startServer();
    this.server.listen();
    this.server.on('remoteCall', (path, queryData) => { // <-- Received a request from http server
      console.log('remoteCall', path, queryData);
      self.pot1.waterTemperature.getTemperature().then(res=>{
        console.log(res);
        this.server.emit('callback', res)
      });
    });
  }
}
const app = new Main();

1 个答案:

答案 0 :(得分:0)

您不想这样做,因为远程调用是针对服务器的-这意味着所有并发连接。您应该做的是根据请求而不是按服务器进行远程调用。

只需在您的请求处理程序中直接调用getTemperature()

import Pot from './app/envoirement/pot/pot';

let pot1 = new Pot({
  potID: 'pot1',
  waterTemperatureProbeID: '28-0114502296aa'
});

class Server {
  constructor(){/* ... */}
  startServer(){
    const self = this;
    self.webserver.on('request', async (req, res) => {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      const q = url.parse(req.url, true);
      if (q.pathname === '/favicon.ico') {
        res.writeHead(200, {'Content-Type': 'image/x-icon'} );
        res.end();
        return;
      }
      const path = q.pathname;
      const queryData = q.query;
      res.write(path);
      res.write(JSON.stringify(queryData));

      pot1.waterTemperature.getTemperature().then(data=>{
        console.log(data);
        res.write(data);
        res.end(); 
      }); 
    });
  }
  listen(){/* ... */}
}

如果您不想在Server中对pot1进行硬编码,则将其作为参数传递给构造函数-一种轻量级的依赖注入:

class Server {
  constructor(pot1){
      this.pot1 = pot1;

      // ...
  }
  startServer(){/* ... */}
  listen(){/* ... */}
}

如果要排除传感器以使其模块化,可以将其保留在自己的模块中:

// sensors.js
let pot1 = new Pot({
  potID: 'pot1',
  waterTemperatureProbeID: '28-0114502296aa'
});

let pot2 = new Pot({
  potID: 'pot2',
  waterTemperatureProbeID: '28-0114504ea1aa'
});

module.exports = {
  pots: [
    pot1,    // the namespacing of this API is really up to you
    pot2
  ]
}

然后您可以将其导入服务器中:

let sensors = require('./sensors');

class Server {
  constructor(){/* ... */}
  startServer(){
    const self = this;
    self.webserver.on('request', async (req, res) => {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      const q = url.parse(req.url, true);
      if (q.pathname === '/favicon.ico') {
        res.writeHead(200, {'Content-Type': 'image/x-icon'} );
        res.end();
        return;
      }
      const path = q.pathname;
      const queryData = q.query;
      res.write(path);
      res.write(JSON.stringify(queryData));

      sensors.pot[1].waterTemperature.getTemperature().then(data=>{
        console.log(data);
        res.write(data);
        res.end(); 
      }); 
    });
  }
  listen(){/* ... */}
}

然后您可以动态查询传感器:

self.webserver.on('request', async (req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  const q = url.parse(req.url, true);
  if (q.pathname === '/favicon.ico') {
    res.writeHead(200, {'Content-Type': 'image/x-icon'} );
    res.end();
    return;
  }
  const path = q.pathname;
  const queryData = q.query;
  const query = queryData.split('&').reduce(function(a,b){
    keyVal = b.split('=');
    a[keyVal[0]] = keyVal[1];
  },{});

  res.write(path);
  res.write(JSON.stringify(queryData));
  sensors.[query.type][query.idx].waterTemperature.getTemperature()
    .then(data=>{
      console.log(data);
      res.write(data);
      res.end(); 
    });
});

现在,您可以发出类似http://server/get?type=pot&idx=0的请求。同样,如果您不想硬编码将传感器导入服务器文件中,则可以始终将其作为参数传递给构造函数。