构造函数中带有Promises的同步代码

时间:2018-08-19 18:32:04

标签: javascript node.js promise es6-promise

我正在使用Node进行一个项目,Node是一种我不太熟悉的语言。

在项目中,我有一个类负责将数据读取和写入数据库(在本例中为LevelDB)。理想情况下,我想在构造函数中同步设置数据库连接,以使方法(writeItem,readItem等)的调用速度不会太快。换句话说,我不希望构造函数返回并允许下一行代码运行,直到所有的诺言都兑现。

我认为我要么缺少该语言的基础知识,要么在节点中有一些我不知道的设计模式。同样失败的玩具示例在这里:

class AClass {
  constructor(n) {
    this.name = n;
    console.log('calling init.');
    this.init();
    console.log('init returned.');
  }

  func() {
    return new Promise(resolve =>  {
      setTimeout(() => {
        resolve(true);
      }, 2000);
    }); 
  }

  async init() {
    console.log('calling func()');
    let x = await this.func();
    console.log('after func(): ');
  }

}


let x = new AClass('test');
console.log(JSON.stringify(x));

这将产生输出:

calling init.
calling func()
init returned.
{"name":"test"}
after func():

这令我惊讶。我本来期望:

calling init.
calling func()
after func():
init returned.
{"name":"test"}

最终目标是实例化一个连接到levelDB实例的类,并且在建立该连接之前不返回该对象。因此,代码可能看起来像这样:

let storage = new StorageObject();
storage.addItem(key, value);   // <-- this fails unless StorageObject
                               //     finishes the db setup.

谢谢! 山姆

3 个答案:

答案 0 :(得分:3)

您的目标是直到连接失效后才返回实例(至少不是这样)。构造函数的工作是创建一个实例并返回该实例。函数需要同步返回 something 。如果它正在执行一些异步操作,则函数可以返回一个Promise,但是您不希望构造函数提供Promise —您需要实例。

执行此操作的简单方法是要求在创建对象后对其进行初始化,然后构造函数可以构造并返回实例,而init函数可以自由返回诺言:

class AClass {
  constructor(n) {/* some setup */}

  func() {
    return new Promise(resolve =>  {
      setTimeout(() => {
        resolve("some name");
      }, 1000);
    }); 
  }

  async init() {
    this.name = await this.func();
    return this
  }

}

new AClass('test').init()
.then((initialized_obj) => console.log(initialized_obj))

如果在节点中执行此操作,则还可以使用eventEmitters在实例初始化后发出事件。像这样:

const EventEmitter = require('events');

class AClass extends EventEmitter{
  constructor(n) {
    super()
    this.init()
    .then(() => this.emit("initialized"))
  }

  func() {
    return new Promise(resolve =>  {
      setTimeout(() => {
        resolve("some name");
      }, 1000);
    }); 
  }

  async init() {
    this.name = await this.func();
    return this
  }

}

let x = new AClass('test')
x.on('initialized', () => console.log(x.name))

答案 1 :(得分:0)

关于代码的两件事:

  1. 当您调用异步函数(this.init())时,它将一直执行直到它返回的await语句,然后将返回promise,控件将转到下一行(console.log('init returned.'))。了解这一点将解决您的困惑。
  2. 等待语句之后的代码(console.log('after func(): ');)仅在等待的诺言已经解决后才执行。

我已将您的代码重新用于您想要做的事情。

    async function AClass(n) {
        let obj = {}
        obj.func = () => {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(true);
                }, 2000);
            });
        };
    
        obj.init = async function () {
            console.log('calling func()');
            let x = await obj.func();
            console.log('after func(): ');
        };
    
        obj.name = n;
        console.log('calling init.');
        await obj.init();
        console.log('init returned.');
        return obj;
    
    }
    
    
    let x = AClass('test');
    x.then((resolveValue) => {
        /*
        *Now the "class" has been instantiated, 
        * code to use the object of the "class" goes here
        * */
        console.log(JSON.stringify(resolveValue));
    });

答案 2 :(得分:0)

如果您确实希望构造函数同步返回有效的AClass,则可以通过稍微重写代码的其他部分来实现。我假设方法writeItemreadItem等是异步的。您所要做的就是重写这些方法,以便它们在继续操作之前等待对象初始化(如有必要)。

例如,假设您的课程如下所示:

class AClass {
  constructor(n) {
    this.name = n;
    this.init();
  }

  async init() {
    ...
  }

  async writeItem(item) {
    return await db.writeItem(item);
  }

  async readItem(itemId) {
    return await db.readItem(itemId);
  }

}

然后您应该能够按照以下方式对其进行重写:

class AClass {
  constructor(n) {
    this.name = n;
    this.awaitInit = this.init();
  }

  async init() {
    ...
  }

  async writeItem(item) {
    await this.awaitInit;
    return await db.writeItem(item);
  }

  async readItem(itemId) {
    await this.awaitInit;
    return await db.readItem(itemId);
  }

}