Javascript : async constructor pattern

寵の児 提交于 2019-12-01 08:15:26

You will have to delay the call to the callback when everything is synchronous because any code in the callback won't be able to reference the object yet (as you've seen). Because the constructor hasn't finished executing, the assignment of it's return value also hasn't finished yet.

You could pass the object to the callback and force the callback to use that reference (which will exist and be fully formed), but you're requiring people to know that they can't use a normally accepted practice so it's much better to make sure the callback is only called asynchronously and people can then write their code in the normal ways they would expect to.

In node.js, it will be more efficient to use process.nextTick() instead of setTimeout() to accomplish your goal. See this article for more details.

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

Here's a general observation. Many people (myself included) think that it overcomplicates things to put any async operation in a constructor for reasons related to this. Instead, most objects that need to do async operations in order to get themselves set up will offer a .init() or .connect() method or something like that. Then, you construct the object like you normally would in a synchronous fashion and then separately initiate the async part of the initialization and pass it the callback. That gets you away from this issue entirely.

If/when you want to use promises for tracking your async operation (which is great feature direction to go), it's way, way easier to let the constructor return the object and the .init() operation to return a promise. It gets messy to try to return both the object and a promise from the constructor and even messier to code with that.

Then, using promises you can do this:

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

If you can't put the object into a Promise, put a promise into the object.

Give it a Ready property that is a promise.

If you have a class hierarchy (I'm talking about Typescript now) of (say) widgets, the base class can define the Ready property and assign it an already resolved Promise object. Doing this is a base class means it's always safe to write code like this

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

Derived classes can take control of promise resolution by replacing the value of Ready with a new promise object, and resolving it when the async code completes.

Here's a comprehensive workup of Asynchronous Constructor design pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!