#javascript #typescript #decorator
Вопрос:
Я пытаюсь создать простой кэш с помощью декоратора методов.
Допустим, у меня есть следующий класс:
class Contract {
constructor(public readonly contractAddress: string) {}
@cache()
async getBalance(): Promise<number> {
// Get balance for this.contractAddress from a backend and return it
return balance
}
}
В настоящее время у меня есть следующий декоратор кэша:
const promiseCache = new Map<string, Promise<unknown>>()
const cache = () => {
return (_target: Object, propertyKey: string, descriptor: PropertyDescriptor) => {
const originalMethod = descriptor.value
descriptor.value = async function (...args: any[]) {
const constructedKey = constructKey(args)
const promise = promiseCache.get(constructedKey)
if (promise) {
return promise
} else {
const newPromise = originalMethod.apply(this, args)
promiseCache.set(constructedKey, newPromise)
return newPromise
}
}
return descriptor
}
}
Это работает нормально, если у меня есть только один экземпляр этого класса. Однако, если я создам второй экземпляр с другим contractAddress
, то кэш, по-видимому, будет таким же.
// Example
const a = new Contract('a') // has balance of 1
const b = new Contract('b') // has balance of 2
await a.getBalance() // returns 1
await b.getBalance() // returns 1
Я попытался найти ответ на эту проблему, но единственное, что я обнаружил, — это то, что невозможно передать экземпляр класса декоратору, поэтому я не могу получить доступ contractAddress
, например, для добавления его в свой ключ.
Я также попытался поместить «Тайник обещаний» на фабрику декораторов, но это тоже не сработало.
Последнее, что я пробовал, — это добавить параметр на фабрику декораторов, но это тоже не сработало.
// THIS DOES NOT WORK
const cache = (id: number) => { ... }
@cache(Math.random())
getBalance() { ... }
// This gave me a random number in the decorator, but it seemed to be the same one per method across all instances
// or
@cache(this.contractAddress) // This doesn't even compile
getBalance() { ... }
Is this possible? The only 2 solutions I see are either not using decorators and adding a cache into every method (which I would like to avoid because of the extra boilerplate), or passing in the contractAddress as an argument for every method, which would also not be nice.