gpt4 book ai didi

Javascript 将带有参数的单例定义为模块

转载 作者:行者123 更新时间:2023-11-30 09:17:30 24 4
gpt4 key购买 nike

我正在尝试在 Javascript 中定义一个单例,以便能够从不同的文件中使用。

class DataService {

constructor(options) {
this.models = options.models ;
this.data = {};
this.refresh();
}

refresh() {
Promise.all([
this.models.DATA.model.findAll({
raw: true,
attributes: ['key', 'value']
})
]).then(([data]) => {
this.data = this.parseData(data);
});
}

parseData(data) {
data.map(x => {
this.data[x.key] = JSON.parse(x.value);
});
return this.data;
}

}

module.exports = (options) => { return new DataService(options) };

我希望能够像这样导入模块

const data = require('dataService')(options);

console.log('data.example', data.example);

我不确定是否可以这样做,因为我使用的是异步方法,并且当我打印日志时数据还没有准备好。

最佳答案

您可以利用模块在所有模块中实现类单例模式的方法是直接导出一个实例。

这样做的原因是 require 在第一次导入后缓存导出,因此将在所有后续导入中返回该实例。

现在您正在导出一个函数,尽管它始终是相同的函数,但它具有始终实例化您的类的新实例的能力,因此打破了您想要实现的单例模式约束(单个实例跨模块)

因为您想在外部指定单例实例化选项,所以您可以通过对代码进行最少更改来实现此目的的一种方法是让导出的函数返回一个实例(如果它已经存在),而不是实例化一个新实例:

let instance; // all files will receive this instance
module.exports = (options) => {
if (!instance) {
// only the first call to require will use these options to create an instance
instance = new DataService(options);
}
return instance;
}

这意味着所有执行 require('dataService')(options) 的文件都将接收相同的实例,无论哪个文件首先导入模块,谁的实例化选项都将应用。

请注意,所有后续调用仍必须采用 require('dataService')() 的形式(注意额外的调用),这看起来像是一种代码味道并且会使代码更难理解。

为了使代码更具可读性,我们可以添加一些冗长的内容:

let instance; // all files will receive this instance
module.exports = {
getInstance(options) {
if (!instance) {
// only the first call to getInstance will use these options to create an instance
instance = new DataService(options);
}
return instance;
}
}

可以这样使用:

const instance = require('dataService').getInstance(options);
const instance = require('dataService').getInstance();
const instance = require('dataService').getInstance();

另一个步骤可能是通过在运行时告诉程序员他们是否错误地使用 API 来使代码更容易被滥用:

if (!instance) {
instance = new DataService(options);
} else if (options) {
// throw error on all subsequent calls with an options arg `require('dataService')(options)`
throw Error('Instance is already instantiate with `options`')
}
return instance;

这不会使代码更具可读性,但会使其更安全一些。

如果我们将您的 API 解释为“任何时候传递选项,我们都应该实例化一个新的单例”,那么您可以考虑维护一个实例集合,通过一些 id 检索(或者甚至可能是选项本身的内存引用) ):

let instances = new Map();
module.exports = (options) => {
if (!instances.has(options.id)) {
instances.set(options.id, new DataService(options));
}
return instances.get(options.id);
}

您的单例中有异步代码这一事实应该无关紧要。时间不是单例的属性,要求只有一个实例。

话虽如此,您可能想要考虑实际返回在您的方法中创建的 promise ,以便您可以正确地链接它们或等待它们:

class DataService {

constructor(options) {
this.models = options.models ;
this.data = {};
this.refresh();
}

refresh() {
return Promise.all(/* ... */).then(/* ... */);
//^^^^^^ return the promise chain so we can react to it externally
}

// ...

}

(async () => {
await new DataService().refresh(); // now you can do this
})()

关于Javascript 将带有参数的单例定义为模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54015088/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com