提问者:小点点

让一个构造函数返回一个promise是不好的做法吗?


我试图为一个博客平台创建一个构造函数,它内部有很多异步操作。这些内容包括从目录中抓取帖子,解析它们,通过模板引擎发送它们等等。

所以我的问题是,让我的构造函数返回一个promise而不是他们调用针对的函数的对象是否不明智。

例如:

var engine = new Engine({path: '/path/to/posts'}).then(function (eng) {
   // allow user to interact with the newly created engine object inside 'then'
   engine.showPostsOnOnePage();
});

现在,用户也可以不提供补充承诺链链接:

var engine = new Engine({path: '/path/to/posts'});

// ERROR
// engine will not be available as an Engine object here

这可能会造成一个问题,因为用户可能会感到困惑,为什么在构建之后不可用。

在构造函数中使用承诺是有道理的。我希望整个博客在建设阶段后发挥作用。但是,在调用之后不能立即访问对象,这似乎是一种气味。

我曾讨论过使用类似的方法,它们将返回承诺。但那些看起来也很臭。

编辑:这是在一个Node.js项目中。


共3个答案

匿名用户

是的,这是一种不好的做法。构造函数应该返回它的类的一个实例,而不是其他的。否则就会弄糟运算符和继承。

而且,构造函数应该只创建和初始化一个新实例。它应该设置数据结构和所有特定于实例的属性,但不执行任何任务。它应该是一个纯粹的功能,没有副作用,如果可能的话,与所有的好处,已经。

如果我想从我的构造函数执行东西呢?

那应该放在你类的一个方法里。你想改变全局状态?然后显式调用该过程,而不是作为生成对象的副作用。此调用可以在实例化之后立即进行:

var engine = new Engine()
engine.displayPosts();

如果该任务是异步的,那么您现在可以很容易地从该方法返回结果的承诺,以便很容易地等待它完成。br>但是,当该方法(异步地)改变实例并且其他方法依赖于此时,我不建议使用这种模式,因为这会导致它们需要等待(即使它们实际上是同步的,也会变成异步的),并且您很快就会进行一些内部队列管理。不要将实例编码为存在但实际上不可用的。

如果我想异步地将数据加载到我的实例中,该怎么办?

问问你自己:你真的需要没有数据的实例吗?你能用一下吗?

如果答案是否定的,那么您不应该在获得数据之前创建它。使数据ifself成为构造函数的参数,而不是告诉构造函数如何获取数据(或传递数据承诺)。

然后,使用静态方法加载数据,并从中返回一个承诺。然后链接一个调用,该调用将数据包装在一个新实例中:

Engine.load({path: '/path/to/posts'}).then(function(posts) {
    new Engine(posts).displayPosts();
});

这使得获取数据的方式具有更大的灵活性,并大大简化了构造函数。类似地,您可以编写静态工厂函数来返回实例的承诺:

Engine.fromPosts = function(options) {
    return ajax(options.path).then(Engine.parsePosts).then(function(posts) {
        return new Engine(posts, options);
    });
};

…

Engine.fromPosts({path: '/path/to/posts'}).then(function(engine) {
    engine.registerWith(framework).then(function(framePage) {
        engine.showPostsOn(framePage);
    });
});

匿名用户

我遇到了同样的问题,想出了这个简单的解决方案。

null

function Engine(path) {
  var engine = this
  engine.initialization = Promise.resolve()
    .then(function () {
      return doSomethingAsync(path)
    })
    .then(function (result) {
      engine.resultOfAsyncOp = result
    })
}

null

Engine.prototype.showPostsOnPage = function () {
  return this.initialization.then(function () {
    // actual body of the method
  })
}

从API使用者的角度来看:

engine = new Engine({path: '/path/to/posts'})
engine.showPostsOnPage()

这是因为您可以将多个回调注册到一个promise,并且它们可以在promise解决后运行,或者如果它已经解决,则在附加回调时运行。

这就是mongoskin的工作原理,除了它实际上并不使用承诺。

编辑:自从我写了这个回复,我就爱上了ES6/7语法,所以这里有另一个使用它的例子。你今天可以和巴别塔一起用。

class Engine {

  constructor(path) {
    this._initialized = this._initialize()
  }

  async _initialize() {
    // actual async constructor logic
  }

  async showPostsOnPage() {
    await this._initialized
    // actual body of the method
  }

}

编辑:您可以在node 7和标志中本机使用此模式!

匿名用户

为了避免关注点的分离,请使用工厂来创建对象。

class Engine {
    constructor(data) {
        this.data = data;
    }

    static makeEngine(pathToData) {
        return new Promise((resolve, reject) => {
            getData(pathToData).then(data => {
              resolve(new Engine(data))
            }).catch(reject);
        });
    }
}