英文:
Getting client is closed on TypeScript class that is used to access Redis
问题
Below is the translated code:
我的类
import * as redis from 'redis';
export class RedisClient {
private client: any
constructor() {
this.client = redis.createClient()
}
public async set(key: string, value: any): Promise<void> {
return new Promise((resolve, reject) => {
this.client.set(key, value, (err: any) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
public async get(key: string): Promise<string> {
return new Promise((resolve, reject) => {
this.client.get(key, (err: any, value: string | PromiseLike<string>) => {
if (err) {
reject(err);
} else {
resolve(value);
}
});
});
}
}
As for your update, using the Singleton pattern to ensure that the RedisClient
is only instantiated once is a common approach when working with resources like Redis connections. This helps avoid the overhead of creating a new connection for each operation.
In TypeScript, the implementation you've shown seems reasonable. By using a static method instance()
and a cached instance, you ensure that only one RedisClient
instance is created and reused throughout the application. This can improve performance and resource usage when dealing with Redis or other similar resources that benefit from connection pooling.
Overall, it's a good pattern to follow when working with resources like Redis, and it aligns with best practices for managing connections efficiently.
英文:
My class
import * as redis from 'redis';
export class RedisClient {
private client: any
constructor() {
this.client = redis.createClient()
}
public async set(key: string, value: any): Promise<void> {
return new Promise((resolve, reject) => {
this.client.set(key, value, (err: any) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
public async get(key: string): Promise<string> {
return new Promise((resolve, reject) => {
this.client.get(key, (err: any, value: string | PromiseLike<string>) => {
if (err) {
reject(err);
} else {
resolve(value);
}
});
});
}
}
I know I need to add this.client.connect()
and I was hoping I could add it to constructor but that doesn't work. I get client is closed. It does work if I add this.client.connect
within the set method but is the approach considered good if we are connecting to Redis every single time before calling set/get?
Is there a better approach to this
Update:
Taking answer into suggestion, created with Singleton pattern. Added the following in RedisClient
let cached: Promise<RedisClient>;
static instance(): Promise<RedisClient> {
if (!cached) cached = RedisClient.create();
return cached;
}
In caller class
constructor() {
this.redisClient = RedisClient.instance();
}
public async handle(event: DynamoDBStreamEvent): Promise<void> {
const client = await this.redisClient;
....
}
This works but I am not too familiar with typescript. Is this considered a good pattern. My intention is that RedisClient is only instantiated once so we are not always re-connecting to Redis with every operation
答案1
得分: 1
这只是JavaScript和TypeScript的限制。.connect
函数是async
的,所以你不能将它放在构造函数中。问题在于,在你的代码其余部分运行之前,.connect
的调用没有被解析。你需要等待.connect
解析完成,然后才能使用Redis客户端。
如果我发现自己编写这种类型的类,通常会添加一个静态方法,该方法将.createClient
和.connect
一起调用,然后使用该方法代替new
。有很多方法可以解决这个问题,但这是其中一种:
import { createClient } from 'redis';
type RedisConnection = ReturnType<typeof createClient>;
export class RedisClient {
private connection: RedisConnection
constructor(connection: RedisClientConnection) {
this.connection = connection
}
static async create() {
const connection = createClient();
connection.on('error', (err) => console.log('Redis Client Error', err));
await connection.connect();
return new RedisClient(connection);
}
public async set(key: string, value: any): Promise<void> {
return this.connection.set(key, value);
}
public async get(key: string): Promise<string> {
return this.connection.get(key);
}
}
请注意以下几点:
- 我将
client
重命名为connection
,因为你正在创建一个名为RedisClient
的类,我认为有两个东西都叫“Client”会令人困惑。 - 我添加了一些错误处理,以便在出现问题时知道发生了什么。这是可选的,但建议使用。您可能还希望进行更健壮的处理。
- 我使用
ReturnType
为你消除了所有的any
! - 我删除了所有的回调/承诺逻辑。Node Redis 4.x 现在支持承诺,因此不再需要它。
你甚至可以进一步采用单例模式,在这里使用,因为对于Node.js应用程序来说,Redis很少需要多个连接。
英文:
This is just a limitation of JavaScript and thus TypeScript. The .connect
function is async
and so you can't put it in the constructor. What's happening is that the resolution of the call to .connect
is not happening before the rest of your code runs. You need to wait for .connect
to resolve before you can use the Redis client.
If I find myself writing this sort of class, I usually add a static method that calls .createClient
and .connect
together and then use that method instead of new
. There's a lot of ways to skin this cat, but here's one:
import { createClient } from 'redis'
type RedisConnection = ReturnType<typeof createClient>
export class RedisClient {
private connection: RedisConnection
constructor(connection: RedisClientConnection) {
this.connection = connection
}
async static create() {
const connection = redis.createClient()
connection.on('error', (err) => console.log('Redis Client Error', err))
await connection.connect()
return new RedisClient(connection)
}
public async set(key: string, value: any): Promise<void> {
return this.connection.set(key, value)
}
public async get(key: string): Promise<string> {
return this.connection.get(key)
}
}
Note a few things here:
- I renamed
client
toconnection
since you are creating a class calledRedisClient
as I thought it would be confusing to have two things called "Client". - I added some error handling so you know what's going on when stuff breaks. Optional, but recommended. And you might want to do something a bit more robust.
- I got rid of the pesky
any
for you usingReturnType
! - I removed all the callback/promise logic. Node Redis 4.x supports promises now so there's no need for it.
You might even take this a step further and use a Singleton pattern here as Redis rarely needs more than one connection for a Node.js application.
答案2
得分: 0
在你的情况下,我会将 Redis 连接包装在 IIF 中,并将结果赋值给类的 client 属性。
import * as redis from 'redis';
export class RedisClient {
private client: any;
constructor() {
(async () => {
this.client = redis.createClient();
await this.client.connect();
})();
}
public async set(key: string, value: any): Promise<void> {
return new Promise((resolve, reject) => {
this.client.set(key, value, (err: any) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
public async get(key: string): Promise<string> {
return new Promise((resolve, reject) => {
this.client.get(key, (err: any, value: string | PromiseLike<string>) => {
if (err) {
reject(err);
} else {
resolve(value);
}
});
});
}
}
请注意,我只翻译了代码部分,没有包括问题或其他内容。
英文:
What i would do in your situation is the wrap the redis connection in a IIF and assign the result to the client property on the class
import * as redis from 'redis';
export class RedisClient {
private client: any
constructor() {
(async()=>{
this.client = redis.createClient();
await this.client.connect()
})()
}
public async set(key: string, value: any): Promise<void> {
return new Promise((resolve, reject) => {
this.client.set(key, value, (err: any) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
public async get(key: string): Promise<string> {
return new Promise((resolve, reject) => {
this.client.get(key, (err: any, value: string | PromiseLike<string>) => {
if (err) {
reject(err);
} else {
resolve(value);
}
});
});
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论