将功能注入其他 Node.js 模块
Inject functionality into other Node.js module
我有一个 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 在承诺链中发出一个 "createAccount" 事件,交出交易对象并让模块注册一些侦听器。
但后来我注意到 EventEmitter 是异步工作的。
Node.js 执行此类操作的最佳做法是什么?
好吧,让我自己回答这个问题:
- 尽管我读到一些不同的东西,但我意识到 EventEmitter 不是异步的
- 所以使用事件完成这个任务基本上是可行的
- 但我遇到了问题,由于数据库调用,我的事件侦听器处于异步状态。因此,我需要一种方法来等待所有事件侦听器 "really" 完成。
- 我在 EventEmitter 的子类中引入了一个 promisedEmit() 函数,它允许侦听器 return 一个承诺和 return 一个承诺本身,只有在所有侦听器承诺完成时才会完成。
这是我想出的代码:
'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(){
//...
});
我有一个 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 在承诺链中发出一个 "createAccount" 事件,交出交易对象并让模块注册一些侦听器。 但后来我注意到 EventEmitter 是异步工作的。
Node.js 执行此类操作的最佳做法是什么?
好吧,让我自己回答这个问题:
- 尽管我读到一些不同的东西,但我意识到 EventEmitter 不是异步的
- 所以使用事件完成这个任务基本上是可行的
- 但我遇到了问题,由于数据库调用,我的事件侦听器处于异步状态。因此,我需要一种方法来等待所有事件侦听器 "really" 完成。
- 我在 EventEmitter 的子类中引入了一个 promisedEmit() 函数,它允许侦听器 return 一个承诺和 return 一个承诺本身,只有在所有侦听器承诺完成时才会完成。
这是我想出的代码:
'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(){
//...
});