jeffijoe / awilix

Extremely powerful Inversion of Control (IoC) container for Node.JS
MIT License
3.6k stars 135 forks source link

[Question] How to resolve with event emiter #78

Closed huyaloha closed 6 years ago

huyaloha commented 6 years ago

Hi @jeffijoe, I'm using Hydra for my project with Awilix. Everything work fine with Awilix and Awilix-express for REST API. However, if the app is fired via hydra.getHydra().on(...), the umfHandler cannot be resolved.

module.exports = class Hydra {
  constructor (opts) {
    this.config = require('./config').HYDRA;
    this.umfHandler = umfHandler; // Resolve handler successfully
  }

  initialize () {
    return new Promise(function (resolve, reject) {
      hydra
        .init(config, function () {
          hydra.registerRoutes(routerConfig);
          hydra.getHydra().on('message', (message) => {
            this.umfHandler(message);  // Undefined
          });
        })
        .then(function () {
          resolve(hydra);
        })
        .catch(function (err) {
          reject(err);
        });
    });
  }
};

Do we have any way to resolve the function like that.

// container.js
container.loadModules(
  [
    ['../src/repository/*.js', Lifetime.SINGLETON],
    ['../src/service/*.js', Lifetime.SCOPED]
  ],
  opts
).register({
  mongoose: asFunction(
    () => require('mongoose').createConnection(config.database.mongodb)
  ).singleton().disposer((conn) => conn.close()),
  umfHandler: asFunction(
    () => require('./umf')
  ).singleton(),
  containerMiddleware: asValue(scopePerRequest(container))
});
jeffijoe commented 6 years ago

Not sure if it's a typo but you're not defining umfHandler anywhere in your constructor.

this.umfHandler = umfHandler; // <- not defined

Maybe you meant this?

this.umfHandler = opts.umfHandler;

Regardless, I'm not sure what you're trying to do? Do you want a scope per event?

huyaloha commented 6 years ago

Sorry, my bad. Yes, typo. Actually and I can resolve successfully on constructor this.umfHandler = opts.umfHandler;

What I would like to achieve are:

I have tried couple ways but cannot resolve the umfHandler inside hydra.getHydra().on(....). So, is it possible to resolve, yes, per event? I have no issue with Express by using awilix-express and inject as middleware (containerMiddleware: asValue(scopePerRequest(container)))

jeffijoe commented 6 years ago

Your problem is actually related to your use of this:

Your this refers to whatever this is in the innermost function, which is no longer your class instance. Test this by console.log(this).

return new Promise(function (resolve, reject) {

And here

.init(config, function () {

If you make them arrow functions, it should work:

module.exports = class Hydra {
  constructor(opts) {
    this.config = require("./config").HYDRA;
    this.umfHandler = umfHandler; // Resolve handler successfully
  }

  initialize() {
    return new Promise((resolve, reject) => {
      hydra
        .init(config, () => {
          hydra.registerRoutes(routerConfig);
          hydra.getHydra().on("message", message => {
            this.umfHandler(message); // Undefined
          });
        })
        .then(function() {
          resolve(hydra);
        })
        .catch(function(err) {
          reject(err);
        });
    });
  }
};

Since you register umfHandler as SINGLETON, resolving per event will have no effect. 😄

huyaloha commented 6 years ago

Thanks for your help, it works.

huyaloha commented 6 years ago

Hi @jeffijoe one more thing, Is it possible to pass the message into function and I still can resolve the related services to do some other businesses.

for example


module.exports = ({message, customerService, notificationService}) => {
  switch (message.typ) {
    case MAIL:
      notificationService(message);
      break;
    case CUSTOMER:
      customerService(message);
      break;
    default:
      logger.debug('Not support message type');
  }
};

// Assume all services have been registered in container.js
jeffijoe commented 6 years ago

I suggest making it a factory function.

module.exports = ({ customerService, notificationService}) => (message) => {
  switch (message.typ) {
    case MAIL:
      notificationService(message);
      break;
    case CUSTOMER:
      customerService(message);
      break;
    default:
      logger.debug('Not support message type');
  }
};
huyaloha commented 6 years ago

Thanks, @jeffijoe. Factory function is great. I have just started developing with Javascript/NodeJs last 3 weeks and it's the first time using factory function.

jeffijoe commented 6 years ago

Happy to help!