异步构造函数

Luk*_*rns 51 javascript constructor asynchronous node.js

我怎样才能最好地处理以下情况?

我有一个构造函数需要一段时间才能完成.

var Element = function Element(name){
   this.name = name;
   this.nucleus = {};

   this.load_nucleus(name); // This might take a second.
}

var oxygen = new Element('oxygen');
console.log(oxygen.nucleus); // Returns {}, because load_nucleus hasn't finished.
Run Code Online (Sandbox Code Playgroud)

我看到三种选择,每种选择都与众不同.

,向构造函数添加回调.

var Element = function Element(name, fn){
   this.name = name;
   this.nucleus = {};

   this.load_nucleus(name, function(){
      fn(); // Now continue.
   });
}

Element.prototype.load_nucleus(name, fn){
   fs.readFile(name+'.json', function(err, data) {
      this.nucleus = JSON.parse(data); 
      fn();
   });
}

var oxygen = new Element('oxygen', function(){  
   console.log(oxygen.nucleus);
});
Run Code Online (Sandbox Code Playgroud)

,使用EventEmitter发出'loaded'事件.

var Element = function Element(name){
   this.name = name;
   this.nucleus = {};

   this.load_nucleus(name); // This might take a second.
}

Element.prototype.load_nucleus(name){
   var self = this;
   fs.readFile(name+'.json', function(err, data) {
      self.nucleus = JSON.parse(data); 
      self.emit('loaded');
   });
}

util.inherits(Element, events.EventEmitter);

var oxygen = new Element('oxygen');
oxygen.once('loaded', function(){
   console.log(this.nucleus);
});
Run Code Online (Sandbox Code Playgroud)

或者三,阻止构造函数.

var Element = function Element(name){
   this.name = name;
   this.nucleus = {};

   this.load_nucleus(name); // This might take a second.
}

Element.prototype.load_nucleus(name, fn){
   this.nucleus = JSON.parse(fs.readFileSync(name+'.json'));
}

var oxygen = new Element('oxygen');
console.log(oxygen.nucleus)
Run Code Online (Sandbox Code Playgroud)

但我以前没有见过这种做法.

我还有其他选择吗?

tim*_*tim 25

更新2:这是使用异步工厂方法的更新示例.注意,如果在浏览器中运行,则需要节点8或Babel.

class Element {
    constructor(nucleus){
        this.nucleus = nucleus;
    }

    static async createElement(){
        const nucleus = await this.loadNucleus();
        return new Element(nucleus);
    }

    static async loadNucleus(){
        // do something async here and return it
        return 10;
    }
}

async function main(){
    const element = await Element.createElement();
    // use your element
}

main();
Run Code Online (Sandbox Code Playgroud)

更新:下面的代码被投票了几次.但是我发现这种方法使用静态方法要好得多:https: //stackoverflow.com/a/24686979/2124586

使用promises的ES6版本

class Element{
    constructor(){
        this.some_property = 5;
        this.nucleus;

        return new Promise((resolve) => {
            this.load_nucleus().then((nucleus) => {
                this.nucleus = nucleus;
                resolve(this);
            });
        });
    }

    load_nucleus(){
        return new Promise((resolve) => {
            setTimeout(() => resolve(10), 1000)
        });
    }
}

//Usage
new Element().then(function(instance){
    // do stuff with your instance
});
Run Code Online (Sandbox Code Playgroud)

  • 我同意.这些天我可能会去一个异步静态函数来加载数据,然后返回填充了数据的实例. (4认同)
  • 关于这一点的奇怪之处在于你的构造函数不会返回实例,它会向实例返回一个承诺,这对于OOP模式来说有点混乱. (3认同)
  • 最优雅的一个. (2认同)
  • 相关:http://stackoverflow.com/questions/24398699/is-it-bad-practice-to-have-a-constructor-function-return-a-promise (2认同)

Jon*_*ski 11

鉴于必须避免在Node中阻塞,事件或回调的使用并不那么奇怪(1).

稍微编辑两个,您可以将它与One合并:

var Element = function Element(name, fn){
    this.name = name;
    this.nucleus = {};

    if (fn) this.on('loaded', fn);

    this.load_nucleus(name); // This might take a second.
}

...
Run Code Online (Sandbox Code Playgroud)

虽然,与fs.readFile您的示例中一样,核心Node API(至少)通常遵循静态函数的模式,这些函数在数据准备好时公开实例:

var Element = function Element(name, nucleus) {
    this.name = name;
    this.nucleus = nucleus;
};

Element.create = function (name, fn) {
    fs.readFile(name+'.json', function(err, data) {
        var nucleus = err ? null : JSON.parse(data);
        fn(err, new Element(name, nucleus));
    });
};

Element.create('oxygen', function (err, elem) {
    if (!err) {
        console.log(elem.name, elem.nucleus);
    }
});
Run Code Online (Sandbox Code Playgroud)

(1)读取JSON文件不应该花很长时间.如果是,也许存储系统的变化是为了数据.