让构造函数返回Promise是不好的做法吗?

2020/10/27 23:02 · javascript ·  · 0评论

我正在尝试为博客平台创建一个构造函数,并且内部进行了许多异步操作。这些范围包括从目录中获取帖子,对其进行解析,通过模板引擎发送它们,等等。

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

例如:

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运算符和继承。

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

如果我想从构造函数中执行操作怎么办?

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

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

如果该任务是异步的,那么您现在可以轻松地从方法中返回其结果的承诺,轻松地等待直到完成为止。

但是,当方法(异步)使实例发生突变并且其他方法依赖于该实例时,我将不推荐这种模式,因为这将导致它们需要等待(即使它们实际上是同步的,也要成为异步的),并且您很快就会拥有一些内部队列管理正在进行。
不要编写实例存在但实际上不可用的代码。

如果要异步将数据加载到实例中怎么办?

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

如果答案是没有,那么你不应该你有数据之前创建它。将数据本身作为构造函数的参数,而不是告诉构造函数如何获取数据(或传递对数据的承诺)。

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

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

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

而不是从构造函数返回Promise,而是将其放在this.initialization属性中,如下所示:

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

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

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,并且它们可以在解析后运行,也可以在附加回调时运行(如果已经解析)。

这是mongoskin的工作方式,除了它实际上没有使用promises。


编辑:自从我写了该答复以来,我就爱上了ES6 / 7语法,因此还有一个使用该语法的示例。您现在可以将其与babel一起使用。

class Engine {

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

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

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

}

编辑:您可以将本模式与节点7和--harmony标志一起使用!

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

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

构造函数的返回值替换了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)
本文地址:http://javascript.askforanswer.com/ranggouzaohanshufanhuipromiseshibuhaodezuofama.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!