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

我正在尝试为博客平台创build一个构造函数,并且里面有许多asynchronous操作。 这些范围从抓取目录中的post,parsing它们,通过模板引擎发送它们等等。

所以我的问题是,让我的构造函数返回一个承诺,而不是他们所谓的newfunction的对象是不明智的。

例如:

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

现在,用户也可能提供补充Promise链接:

 var engine = new Engine({path: '/path/to/posts'}); // ERROR // engine will not be available as an Engine object here 

这可能会造成问题,因为用户可能会困惑为什么 engine 在施工后不可用。

在构造函数中使用Promise的原因是有道理的。 我希望整个博客在施工阶段后能够正常运行。 但是,在调用new之后,似乎几乎不能立即访问对象。

我讨论过使用engine.start().then()engine.init()这样的方法来代替Promise。 但是那些也似乎是臭的。

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

是的,这是一个不好的做法。 构造函数应该返回它的类的一个实例,没有别的。 这会弄乱new运营商 ,否则inheritance。

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

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

这应该在你的class级的方法。 你想改变全局状态? 然后明确地调用该过程,而不是作为生成对象的副作用。 这个调用可以在实例化之后进行:

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

如果这个任务是asynchronous的,你现在可以很容易地从方法中返回一个promise的结果,很容易等到它完成。
但是,如果方法(asynchronous)改变实例和其他方法依赖的方式,我会不推荐这种模式,因为这会导致它们被要求等待(即使它们实际上是同步的,也会变成asynchronous),而且你很快就会一些内部队列pipe理正在进行。 不要编码实例存在,但实际上是不可用的。

如果我想asynchronous加载数据到我的实例呢?

问问自己: 你真的需要没有数据的实例吗? 你能以某种方式使用它吗?

如果答案是否定的 ,那么在获得数据之前不应该创build它。 将数据作为parameter passing给构造函数,而不是告诉构造函数如何获取数据(或传递数据承诺)。

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

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

这使获取数据的方式具有更大的灵活性,并简化了构造函数。 同样,你可以编写一些静态工厂函数来返回Engine实例的promise:

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

我遇到了同样的问题,并提出了这个简单的解决scheme。

把它放在这个this.initialization属性中,而不是从构造函数中返回一个Promise,如下所示:

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

然后,将每个方法包装在初始化后运行的callback中,如下所示:

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

它从API消费者angular度看起来如何:

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

这是有效的,因为你可以注册多个callback到一个承诺,他们运行后,或者,如果它已经解决,在附加callback时。

这是mongoskin如何工作,除非它实际上没有使用承诺。


编辑:因为我写了这个答复,我已经爱上了ES6 / 7的语法,所以还有另一个例子使用它。 你今天可以用babel使用它。

 class Engine { constructor(path) { this.initialization = (async () => { this.resultOfAsyncOp = await doSomethingAsync(path) })() } async showPostsOnPage() { await this.initialization // actual body of the method } } 

编辑 :你可以用节点7和--harmony标志原生地使用这个模式!

为了避免问题的分离,请使用工厂创build对象。

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

构造函数的返回值replace了new运算符刚生成的对象,因此返回promise不是一个好主意。 以前,来自构造函数的显式返回值被用于单例模式。

ECMAScript 2017中更好的方法是使用静态方法:您有一个进程,即静态数字。

在构造函数之后,在新对象上运行的哪个方法可能只能被类本身所知晓。 要将其封装在类中,可以使用process.nextTick或Promise.resolve,推迟进一步的执行,允许在Process.launch(构造函数的调用者)中添加侦听器以及其他内容。

由于几乎所有的代码都在Promise中执行,所以错误最终会在Process.fatal中出现

这个基本的想法可以修改,以适应特定的封装需求。

 class MyClass { constructor(o) { if (o == null) o = false if (o.run) Promise.resolve() .then(() => this.method()) .then(o.exit).catch(o.reject) } async method() {} } class Process { static launch(construct) { return new Promise(r => r( new construct({run: true, exit: Process.exit, reject: Process.fatal}) )).catch(Process.fatal) } static exit() { process.exit() } static fatal(e) { console.error(e.message) process.exit(1) } } Process.launch(MyClass)