将功能注入其他Node.js模块

时间:2015-11-01 15:40:23

标签: javascript node.js dependency-injection sequelize.js

我有一个Node.js应用程序(使用sequelize作为orm),我尝试将其分成不同的模块,这些模块可以很容易地重复使用。这意味着我需要删除它们之间的一些依赖关系,但我在某一点上挣扎:

有一个名为“帐户”的模块,它应该(几乎)对其他模块具有零依赖性。它只提供基本的帐户功能。

然后还有其他模块,它们依赖于帐户模块(没关系)。

目前,帐户模块依赖于其他模块,我想解决这些模块。现在,当创建帐户时,其他模块也必须在数据库中创建一些对象。这应该发生在同一个事务中,并且帐户模块不需要了解其他模块。

目前,这看起来如下:

AccountController.prototype.createAccount = function (data) {
   // validation checks etc. omited

   return db.sequelize.transaction(function (t) {
      return Q.Promise(function (resolve, reject, notify) {
         _createHash(pw, 10)
            .then(function (hash) {
               data.passwordHash = hash;

               return _createAccount(data, t);
            })
            .then(function (account) {
               // create user
               return [account, _createUser(account, t)];
            })
            .spread(function (account, user) {
               // create another object
               return [account, user, _createXXX(account, t)]
            })
            .spread(function(account, user, xxx) {
                // create 1 more object
               return [account, user, xxx, _createYYY(account, t)];
            })
            .spread(function (account, user, xxx, yyy) {
               resolve([account, user, xxx, yyy]);
            })
            .catch(reject);
      });

   });
};

现在,我只想在这个模块中创建Account对象,让其他模块独立创建它们的对象,但是在同一个事务中。

首先我想到让AccountController在promise链中发出一个“createAccount”事件,移交事务对象并让模块注册一些监听器。 但后来我注意到EventEmitter是异步工作的。

Node.js做这样的事情的最佳做法是什么?

1 个答案:

答案 0 :(得分:0)

好的,让我自己回答一下这个问题:

  • 尽管我读了不同的东西,但我意识到EventEmitter并非异步
  • 因此,使用事件执行此任务基本上可以正常工作
  • 但是我遇到了问题,我的事件监听器是异步的,因为数据库调用。因此,我需要一种方法来等待所有事件监听器“真正”完成。
  • 我向EventEmitter的子类引入了一个promisedEmit()函数,它允许一个侦听器返回一个promise并返回一个promise本身,它只会在所有侦听器的promise完成后才能完成。

这是我提出的代码:

'use strict';

var EventEmitter = require('events');
var util = require('util');
var Q = require('q');

function PromisedEventEmitter() {
}

util.inherits(PromisedEventEmitter, EventEmitter);

PromisedEventEmitter.prototype.promisedEmit = function promisedEmit(type) {
   var er, handler, len, args, i, listeners;
   var promises = [];

   if (!this._events)
      this._events = {};

   // If there is no 'error' event listener then throw.
   if (type === 'error' && !this._events.error) {
      er = arguments[1];
      if (this.domain) {
         if (!er)
            er = new Error('Uncaught, unspecified "error" event.');
         er.domainEmitter = this;
         er.domain = this.domain;
         er.domainThrown = false;
         this.domain.emit('error', er);
      } else if (er instanceof Error) {
         throw er; // Unhandled 'error' event
      } else {
         throw Error('Uncaught, unspecified "error" event.');
      }
      return Q.all(promises);
   }

   handler = this._events[type];

   if (util.isUndefined(handler))
      return Q.all(promises);

   if (this.domain && this !== process)
      this.domain.enter();

   var promise;
   if (util.isFunction(handler)) {
      switch (arguments.length) {
         // fast cases
         case 1:
            promise = handler.call(this);
            break;
         case 2:
            promise = handler.call(this, arguments[1]);
            break;
         case 3:
            promise = handler.call(this, arguments[1], arguments[2]);
            break;
         // slower
         default:
            len = arguments.length;
            args = new Array(len - 1);
            for (i = 1; i < len; i++)
               args[i - 1] = arguments[i];
            promise = handler.apply(this, args);
      }
      promises.push(promise);
   } else if (util.isObject(handler)) {
      len = arguments.length;
      args = new Array(len - 1);
      for (i = 1; i < len; i++)
         args[i - 1] = arguments[i];

      listeners = handler.slice();
      len = listeners.length;
      for (i = 0; i < len; i++) {
         promise = listeners[i].apply(this, args);
         promises.push(promise);
      }
   }

   if (this.domain && this !== process)
      this.domain.exit();

   return Q.all(promises);
};


module.exports = PromisedEventEmitter;

这基本上只是原始的.emit代码,我只添加了一些承诺处理。我现在可以使用与原始EventEmitter相同的方式,只需调用

XXXController
    .promisedEmit("eventName", arg1, arg2, argN)]
    .then(function(){
        //...
    });