Nodejs要求类返回{}

时间:2018-06-18 14:43:54

标签: javascript node.js es6-modules

所以我正在创建一个单例类,当我从我的server.js文件中需要它时它工作正常,但是当我从另一个文件中需要它时,它返回为undefined。我会尝试发布相关代码,但有些代码必须由于工作而被删除。

server.js

const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const http = require('http');
const app = express();
const config = require('config');
const FBConfigsListener = require('./server/amq_listeners/fb_configs.listener');
const FBConfigs = require('./server/models/FBConfigs');

//Api file for interacting with mongodb
const api = require('./server/routes/api.routes');

//Parsers
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

//Angular Dist output folder
app.use(express.static(path.join(__dirname, 'dist')));

//Api location
app.use('/api', api);

//Send all  other requests to angular
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist/index.html'));
});

//set port
var port = config.get('webserver.port');
app.set('port', port);

const server = http.createServer(app);

server.listen(port, () => console.log(`Running on localhost:${port}`));

模型/ FBConfigs.js

var ConfigModel = require('./config');
var config = require('config');
var _ = require('lodash');
var FBConfigsListener = require('../amq_listeners/fb_configs.listener');
var AMQAdapter = require('../adapters/amq.adapter');
var uniqid = require('uniqid');
const connectionOptions = config.get('activemq.connectionOptions');

class FBConfigs {
  constructor() {
    console.log(config.get('environments'));
    this.listener = FBConfigsListener;
    this.configs = {};
    this.unique_keys = ['id'];
    this.update_topic = '/topic/fusebuilder.update.config.';
    console.log(FBConfigsListener);
    //AMQ Client
    this.amq_client = AMQAdapter.getInstance(connectionOptions.host, connectionOptions.port);
  }

  add(key, config) {
    if (!(key in this.configs)) {
      this.configs[key] = new ConfigModel(this.unique_keys);
    }
    this.configs[key].add(config);
  }

  get(key) {
    let configs_json = {};
    if (key) {
      configs_json[key] = JSON.parse(this.configs[key].toString());
    } else {
      for (let key in this.configs) {
        configs_json[key] = JSON.parse(this.configs[key].toString());
      }
    }
    return configs_json;
  }

  updateByID(key, id, input_config) {
    let configs = this.configs[key].get();
    for (let config of configs) {
      if (input_config.id === config.id) {
        this.update(key, _.merge(config, input_config));
      }
    }
  }
  //Send update to config topic
  update(key, config) {
    let topic = this.update_topic + key;
    var update_object = {};
    if (Array.isArray(config)) {
      update_object[key] = [...config];
    } else {
      update_object[key] = [config];
    }
    console.log(`Sending ${key} update:${JSON.stringify(update_object)}`);
    this.amq_client.sendMessage(topic, update_object);
  }

  copyTo(key, id, env) {
    let selected_env = config.get('environments.' + env);
    // let tmp_amq_client = new AMQAdapter(selected_env.host, selected_env.port);
    let selected_config = this.configs[key].getByID(id);
    console.log(this);
    if (key === 'fuses') {
      console.log('In FBConfig Copy to for fuses');
      const get_fuse_topic = '/topic/fusebuilder.get_fuse';
      const tran_id = uniqid();
      const sendObj = { fuseName: id, tran_id };
      this.amq_client.sendMessage(get_fuse_topic, sendObj);
      var startTime = process.hrtime()[0];
      var timeout = false;
      while (!this.listener.get_copy_fuse_data(tran_id)) {
        console.log('Waiting for config');
        sleep(100);
        if (process.hrtime()[0] - startTime > 3) {
          console.log('Timed out');
          timeout = true;
          break;
        }
      }
      console.log(JSON.stringify(FBConfigsListener.get_copy_fuse_data(tran_id)));
    } else {
      tmp_amq_client.sendMessage(this.update_topic, selected_config);
    }

    console.log(`Copy ${key} id:${id} to ${env}`);
  }
}

module.exports = new FBConfigs();

amq_listener / fb_configs.listener.js

const config = require('config');
var AMQAdapter = require('../adapters/amq.adapter');
var FBConfigs = require('../models/FBConfigs');
**removed for work**

class FBConfigsListener {
  constructor() {
    this.instance;
    this.copy_fuse_data = {};
    //AMQ Client
    this.amq_client = AMQAdapter.getInstance(connectionOptions.host, connectionOptions.port);
    //Subscribe to configs
    this.amq_client.subscribe(config_subscribe_topic, this.config_topic_callback.bind(this));
    //Request Tables
    this.amq_client.sendMessage(config_request_topic, { tables: config_tables });
    //Subscribe to Copy Fuse topic
    this.amq_client.subscribe(subscribe_fuse_copy_topic, this.copy_fuse_callback.bind(this));
  }

  config_topic_callback(err, message) {
    let dest = this.amq_client.getDestination(message);
    let key = this.get_key_from_topic(dest);
    this.amq_client.readMessage(message, body => {
      let configs = JSON.parse(body);
      if (key in configs) {
        for (let config of configs[key]) {
          FBConfigs.add(key, config);
        }
      }
    });
  }

  copy_fuse_callback(err, message) {
    this.amq_client.readMessage(message, body => {
      const config = JSON.parse(body);
      this.copy_fuse_data[config.tran_id] = config;
    });
  }

  //Get Key from the topic and convert using key map if needed
  get_key_from_topic(topic) {
    let key = topic.split('.')[topic.split('.').length - 1];
    key = key in key_map ? key_map[key] : key;
    return key;
  }

  get_copy_fuse_data(id) {
    if (id in this.copy_fuse_data) {
      return this.copy_fuse_data[id];
    } else {
      return false;
    }
  }
}

module.exports = new FBConfigsListener();

FBConfigs发生错误。 FBConfigsListener返回{},因此其中的所有函数都是未定义的。即使我console.log(require('../amq_listeners/fb_configs.listener'))它打印{}但是在server.js中执行相同的操作(使用更新的路径)它会打印模块。

此外,我们也将了解如何改进编码风格的提示。

修改 所以我发现这些类之间存在循环依赖关系。如何解决这个问题,同时允许我从另一个人那里打电话。

2 个答案:

答案 0 :(得分:1)

这是由循环依赖引起的。你应该避免它或非常小心地使用它。

在你的情况下,修复可能非常简单,将行var FBConfigs = require('../models/FBConfigs');从文件末尾的侦听器移动到最后一行(是的,甚至在module.exports之后)。

编辑:实际上它可能还不够,因为我更详细地检查了代码。由于您没有在FBConfig构造函数中使用Listener,您可以创建方法assignListener,从该构造函数中删除this.listener并稍后在server.js中调用它,这将执行this.listener

或者最后一个解决方案,这也是"最佳实践"。不要导出实例。仅导出类。然后在server.js中,在两者都需要之后创建这些实例。

答案 1 :(得分:1)

我建议你首先实例化你的依赖项,然后将它们存储在一些你可以传递给你的依赖类的对象中。结构可以是

工厂/ services.js

/*
 * Instantiates passed services and passes injector object to them
 */
module.exports = function createServices(injector, services) {
  return Object.entries(services)
    .reduce((aggregator, [name, serv]) => {
      const name_ = camelCase(name);

      aggregator.set(name_, new serv(injector));

      return aggregator;
  }, new Map());
};

LIB / service.js

/**
 * Base class for classes need any injections
 */
module.exports = class Service {
  constructor(injector) {
    this.injector = injector;
  }

  get dependencies() {
    return this.injector.dependencies;
  }

  /*
   * Background jobs can be ran here
   */
  async startService() {}

  /*
   * Background jobs can be stopped here
   */
  async stopService() {}
};

LIB / injector.js

const Service = require('./service');

/*
 * Contains all dependencies
 */
module.exports = class Injector {
  constructor() {
    this.services = new Map();
    this._dependencies = {};
  }

  has(name) {
    return this.services.has(name);
  }

  register(name, service) {
    if (this.has(name)) {
      throw new Error(`Service ${name} already exists`);
    }

    if (service instanceof Service === false) {
      throw new Error('Argument #2 should be an instance of Service');
    }

    this.services.set(name, service);
    this._dependencies[name] = service;
  }

  unregister(name) {
    if (! this.has(name)) {
      throw new Error(`Service ${name} not found`);
    }

    this.services.delete(name);
    delete this._dependencies[name];
  }

  get dependencies() {
    return { ...this._dependencies };
  }

  /*
   * Starts all registered services
   */
  async start() {
    for (let service of this.services.values()) {
      await service.startService();
    }
  }

  /*
   * Stops all registered services
   */
  async stop() {
    for (let service of this.services.values()) {
      await service.stopService();
    }
  }
};

然后导入,初始化并绑定主文件中的服务(不要忘记导出一个类,而不是像现在这样导出的对象)。

server.js

const createServices = require('./factories/services.js');
const injector = require('./lib/injector');
const Injector = new injector();

const services = createServices(Injector, [require('./server/amq_listeners/fb_configs.listener'), require('./server/models/FBConfigs')]);

services.forEach((service, name) => {
  Injector.register(name, service);
});

// Start services
Injector.start();

将所需的类继承到Service类,您将获得对所有依赖项的访问权限(不要忘记从构造函数中调用super())。像

模型/ FBConfigs.js

const Service = require('../lib/service');    

class FBConfigs extends Service {
  constructor(injector) {
     super(injector);
     const { FBConfigsListener } = this.dependencies;
     ...your code here
  }

  async startService() {
    ...run bg job or init some connection
  }

  async stopService() {
    ...stop bg job or close some connection
  }
}

module.exports = FBConfigs;

此外,您可以将一些配置对象传递给createServices(我没有在此处包含它),其中的密钥等于包含配置对象的服务名称和值,并将配置传递给适当的服务。