我需要异步实例化一个对象。但是构造函数方法不能被声明为asyncasync constructor(){}
,也不能像async new MyClass()
那样被调用。无论如何,这都会很奇怪。因此,需要某种类型的工厂。我还想隐藏构造函数。如果构造函数可以被声明为private:#constructor
,那么这将非常容易实现。但即使有ES2022私有类功能,这也是不可能的。然而,有几种模式可以解决这两个问题。我将列出其中的3个(在ES6模块的上下文中(。
我想知道哪种模式(如果有的话(被认为是好的,哪种不好。或者,如果有更好的解决方案,请告诉我。
方法一:静态布尔值(无工厂(
let calledInit = false;
export default class MyClass {
constructor(data) {
if (calledInit === false)
throw Error('private constructor');
calledInit = false;
// member initializations here
}
static async initialize() {
calledInit = true;
// do some async stuff here
return new MyClass(data);
}
}
方法二:闭包和单例工厂
const MyClass = (function() {
class MyClass {
constructor(data) {
// member initializations here
}
}
return {
initialize: async function() {
// do some async stuff here
return new MyClass(data);
}
}
})();
Object.freeze(MyClass);
export default MyClass;
像这样使用它们:
const obj = await MyClass.initialize();
方法三:工厂功能
class MyClass {
constructor(data) {
// member initializations here
}
}
export default async function MyClassInitializer() {
// do some async stuff here
return new MyClass(data);
}
像这样使用:
const obj = await MyClassInitializer();
编辑:为了澄清,我认为隐藏构造函数是有益的。主要用于调试。假设构造函数是公共的,而其他人正在使用新运算符实例化对象。它总是以模糊的错误消息失败,她/他无法理解。所有这一切,因为她/他没有仔细阅读文档,其中明确说明使用initialize((方法创建实例。这本来可以很容易地通过隐藏构造函数来防止。
免责声明这是我的观点
异步实例化对象并不是一件有充分理由的事情。
构造函数的目的是构造您的对象。不应该在构造函数中放入诸如文件、API等的加载操作。
将它们放入一个单独的函数中,并将结果用作类的参数。
async () => {
const data = await fetch(...) // get data / do some sideeffect
const myObject = new MyObject(data)
}
这通常也是可取的,因为您可以更新一些状态来跟踪请求的进度。或者,您可能希望缓存它并在多个位置使用它。
async () => {
state = "LOADING"
const data = await fetch(...) // get data / do some sideeffect
state = "SUCCESS"
const myObject = new MyObject(data)
}
隐藏构造函数并不是真正需要的,因为当你不向它传递数据时会出现错误。类的用户应该知道要传递什么。如果你使用typescript,这将更加严格。
当然,您可以像方法3中那样将其放在一个初始化函数中。