【问题标题】:Inject functionality into other Node.js module将功能注入其他 Node.js 模块
【发布时间】:2015-11-01 15:40:23
【问题描述】:

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

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

然后还有其他模块,对account模块有依赖(没关系)。

目前,Account 模块依赖于其他模块,我想解决这个问题。现在,当创建帐户时,其他模块也必须在数据库中创建一些对象。这应该发生在同一个事务中,并且帐户模块不知道其他模块的任何信息。

目前,如下所示:

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 中执行此类操作的最佳实践是什么?

【问题讨论】:

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


    【解决方案1】:

    好吧,让我自己回答这个问题:

    • 尽管我读到了一些不同的东西,但我意识到 EventEmitter 不是异步的
    • 所以使用事件来完成这个任务基本上是可行的
    • 但我遇到了一个问题,即我的事件侦听器在异步位置上,由于数据库调用。因此,我需要一种方法来等待所有事件侦听器“真正”完成。
    • 我在 EventEmitter 的子类中引入了 promiseEmit() 函数,它允许侦听器返回一个 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 代码,我只是在其中添加了一些 Promise 处理。我现在可以像原来的 EventEmitter 一样使用它,只需调用

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-25
      • 2014-09-05
      • 1970-01-01
      • 2018-10-21
      • 2021-08-21
      • 1970-01-01
      相关资源
      最近更新 更多