我正在定义一个类,它实例化几个依赖于先前模块的模块。模块本身可能需要异步操作才能准备好(即建立 mysql 连接),因此我为每个构造函数提供了一个回调,一旦模块准备好就会调用。但是我在实例化立即准备好的类时遇到了问题:
var async = require('async');
var child = function(parent, cb) {
var self = this;
this.ready = false;
this.isReady = function() {
return self.ready;
}
/* This does not work, throws error below stating c1.isReady is undefined*/
cb(null, true);
/* This works */
setTimeout(function() {
self.ready = true;
cb(null, true);
}, 0);
}
var Parent = function(cb) {
var self = this;
async.series([
function(callback){
self.c1 = new child(self, callback);
},
function(callback){
self.c2 = new child(self, callback);
}
],
function(err, results){
console.log(self.c1.isReady(), self.c2.isReady);
console.log(err, results);
});
}
var P = new Parent();
我猜问题是在构造函数中调用 cb 意味着异步会在构造函数完成之前继续执行下一个函数。有更好的方法吗?我考虑过使用 promise ,但我发现这种方法更容易理解/遵循。
最佳答案
当一切都同步时,您将不得不延迟对回调的调用,因为回调中的任何代码都还不能引用该对象(如您所见)。因为构造函数还没有执行完,所以对它的返回值的赋值也还没有完成。
您可以将对象传递给回调并强制回调使用该引用(它将存在并完全形成),但是您需要人们知道他们不能使用通常接受的做法,所以这很重要最好确保只异步调用回调,然后人们可以按照他们期望的正常方式编写代码。
在 node.js 中,使用 process.nextTick()
代替 setTimeout()
会更有效地实现您的目标。参见 this article了解更多详情。
var child = function(parent, cb) {
var self = this;
this.ready = false;
this.isReady = function() {
return self.ready;
}
/* This works */
process.nextTick(function() {
self.ready = true;
cb(null, true);
}, 0);
}
这是一般性的观察。出于与此相关的原因,许多人(包括我自己)认为将任何异步操作放在构造函数中会使事情过于复杂。相反,大多数需要执行异步操作以进行 self 设置的对象将提供 .init()
或 .connect()
方法或类似方法。然后,您像往常一样以同步方式构造对象,然后单独启动初始化的异步部分并将回调传递给它。这让你完全远离这个问题。
如果/当你想使用 promises 来跟踪你的异步操作(这是一个很好的功能方向),让构造函数返回对象和 .init()
返回 promise 的操作。尝试从构造函数返回对象和 promise 会变得很困惑,使用它进行编码甚至更困惑。
然后,使用 promises 你可以这样做:
var o = new child(p);
o.init().then(function() {
// object o is fully initialized now
// put code in here to use the object
}, function(err) {
// error initializing object o
});
关于Javascript : async constructor pattern,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28227480/