【问题标题】:Javascript : async constructor patternJavascript:异步构造函数模式
【发布时间】:2015-03-29 10:22:36
【问题描述】:

我正在定义一个类,它实例化几个依赖于先前模块的模块。模块本身可能需要在准备好之前进行异步操作(即建立 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 意味着 async 在构造函数完成之前继续执行下一个函数。有更好的方法吗?我考虑过使用 Promise,但我发现这种方法更容易理解/遵循。

【问题讨论】:

标签: javascript node.js async.js


【解决方案1】:

当一切都同步时,您将不得不延迟对回调的调用,因为回调中的任何代码都无法引用该对象(如您所见)。因为构造函数还没有执行完,所以它的返回值的赋值也还没有完成。

您可以将对象传递给回调并强制回调使用该引用(它将存在并完全形成),但您要求人们知道他们不能使用通常接受的做法,所以它很多最好确保回调只被异步调用,然后人们可以按照他们期望的正常方式编写代码。

在 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);
}

这是一个一般性的观察。许多人(包括我自己)认为,出于与此相关的原因,将任何异步操作放入构造函数中会使事情变得过于复杂。相反,大多数需要执行异步操作以进行设置的对象将提供.init().connect() 方法或类似的方法。然后,您像往常一样以同步方式构造对象,然后分别启动初始化的异步部分并将回调传递给它。这让你完全摆脱了这个问题。

如果/当您想使用 Promise 来跟踪您的异步操作(这是一个很好的功能方向),让构造函数返回对象并让 .init() 操作返回 Promise 会更容易。尝试从构造函数返回对象和 Promise 会变得很麻烦,甚至用它来编写代码会更麻烦。

然后,使用 Promise 你可以这样做:

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
});

【讨论】:

    【解决方案2】:

    如果不能将对象放入 Promise,则将 Promise 放入对象中。

    给它一个 Ready 属性,它是一个承诺。

    如果您有(比如说)小部件的类层次结构(我现在谈论的是 Typescript),则基类可以定义 Ready 属性并为其分配一个已解析的 Promise 对象。这样做是一个基类意味着编写这样的代码总是安全的

    var foo = Foo();
    foo.Ready.then(() => {
      // do stuff that needs foo to be ready
    });
    

    派生类可以通过将 Ready 的值替换为新的 Promise 对象来控制 Promise 解析,并在异步代码完成时解析它。

    这是Asynchronous Constructor design pattern的全面检查

    【讨论】:

      猜你喜欢
      • 2014-10-28
      • 1970-01-01
      • 2012-08-05
      • 2013-12-27
      • 2018-06-26
      • 1970-01-01
      • 2017-04-14
      • 2011-03-14
      • 1970-01-01
      相关资源
      最近更新 更多