有时,我们可能只想缓存某个方法的返回值一段时间。 打字稿装饰器非常方便。 我们可以创建一个自定义装饰器,并将其放置在方法上并使用它来完成。
@Cache({
duration : 10000
})
method2(){
return new Promise ( resolve => {
setTimeout( () => resolve( Math .random()), 1000 );
});
}
“装饰器是一种特殊的声明,可以附加到类声明 , 方法 , 访问器 , 属性或参数上 。装饰器使用@expression形式,其中表达式必须求值,该函数将在运行时调用有关以下信息的函数:装饰的宣言。”
让我们开始吧!
@Cache()
method1(){
return Math .random();
}
export function Cache ( ) {
let originalFunc: Function ;
return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) {
originalFunc = descriptor.value;
descriptor.value = function ( ) {
console .log( 'time: ' , new Date ());
console .log( 'why is this doing??' )
};
};
我们使用了descriptor.value
,其中包含具有装饰器的函数/方法。 这是我们的原始功能。 我们将其放在一个变量中,即originalFunc
(原始名称是!),并提供一个新功能! 因此,当调用该函数而不是我们的原始函数时,将改为调用该函数!
当然,我们也可以在这里调用原始函数。
export function Cache ( ) {
let originalFunc: Function ;
return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) {
originalFunc = descriptor.value;
descriptor.value = function ( ) {
console .log( 'time: ' , new Date ());
return originalFunc();
};
};
}
因此,现在唯一的区别是此装饰器在运行原始函数之前会显示时间戳(原始函数只是返回一个随机数)。
让我们实际缓存值!
export function Cache ( ) {
let originalFunc: Function ;
let value: any;
return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) {
originalFunc = descriptor.value;
descriptor.value = function ( ) {
if (value){
console .log( 'from cache' )
return value;
}
console .log( 'caching' )
value = originalFunc();
return value;
};
};
}
我们添加了一个名为value
的新变量。 我们检查是否定义了值,如果已定义,则返回该值。 如果不是,请调用原始函数以获取该值并返回它。
现在,我们有一个问题,它将无限期缓存。 我们可能不想永远缓存它。 相反,如果我们可以决定将其缓存多长时间会更好。
让我们为参数添加一个接口。 目前,我们只有持续时间,但如果需要,可以添加更多时间。 我们还创建了一个具有默认值的对象,因此,如果不传入,我们可以提供一些默认值。
export interface CacheOptions { // we may add additional parameters here
duration?: number
}
export function Cache ( params: CacheOptions = {} ) {
const defaultValues: Partial<CacheOptions> = {
duration : 3000 ,
}
params = {
...defaultValues,
...params
};
...
现在,我们需要计算到期时间。
...
let originalFunc: Function ;
let value: any;
let cacheUntil: Date ;
return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) {
originalFunc = descriptor.value;
descriptor.value = function ( ) {
const now = new Date ();
if (value && cacheUntil && cacheUntil > now) {
console .log( "from cache" );
return value;
}
console .log( 'caching' )
value = originalFunc();
cacheUntil = new Date (now.getTime() + params.duration);
return value;
};
};
}
除了检查值之外,我们还检查cacheUntil是否在现在之前。 如果是这样,则返回缓存的值。 否则,在获取新值之后,我们将计算cacheUntil值。
如您所见,它现在并不是无限期地缓存。 它每3秒失效一次。
可观察/承诺如何?
在添加更多代码之前,我将把缓存逻辑放入一个内联函数中,以便我们可以在多个地方调用而无需离开函数范围。
...
const cacheValue = ( val, now )=> {
console .log( "caching " );
cacheUntil = new Date (now.getTime() + params.duration);
value = val;
};
return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) {
originalFunc = descriptor.value;
descriptor.value = function ( ) {
const now = new Date ();
if (value && cacheUntil && cacheUntil > now) {
console .log( "from cache" );
return value;
}
console .log( 'caching' )
value = originalFunc();
cacheValue(value, now);
return value;
};
};
...
当我们调用原始函数时,返回类型可以是一个值(我们假设到目前为止是一个值),也可以是promise或可观察的。
...
const result = originalFunc();
if (result instanceof Observable){
funcType = 'observable' ;
return result.pipe(
tap( val => {
cacheValue(val, now);
}));
} else if ( result instanceof Promise ){
funcType = 'promise' ;
return result
.then( value => {
cacheValue(value, now);
return value;
});
} else {
funcType = 'value' ;
cacheValue(result, now);
return result;
}
...
我们检查结果实例以确定其类型。 当我们返回缓存的值时,我们添加了一个名为funcType的新变量,因为我们将需要它。 根据每种类型,我们缓存值并以一致的方式返回值。
为了返回缓存的值:
...
if (value && cacheUntil && cacheUntil > now) {
console .log( "from cache" );
switch (funcType){
case "observable" : return of (value);
case "promise" : return Promise .resolve(value);
default : return value;
}
}
...
在实际情况下, originalFunc()
可能会引发与this
关键字相关的错误。 让我们将其替换为originalFunc.apply(this);
。 如果我们的方法有参数,则可以使用originalFunc.apply(this, args);
代替。
最后:
export interface CacheOptions { // we may add additional parameters here
duration?: number
}
export function Cache ( params: CacheOptions = {} ) {
const defaultValues: Partial<CacheOptions> = {
duration : 3000 ,
}
params = {
...defaultValues,
...params
};
let originalFunc: Function ;
let value: any;
let cacheUntil: Date ;
let funcType: string;
const cacheValue = ( val, now )=> {
console .log( "caching " );
cacheUntil = new Date (now.getTime() + params.duration);
value = val;
};
return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) {
originalFunc = descriptor.value;
descriptor.value = function ( ) {
const now = new Date ();
if (value && cacheUntil && cacheUntil > now) {
console .log( "from cache" );
switch (funcType){
case "observable" : return of (value);
case "promise" : return Promise .resolve(value);
default : return value;
}
}
const result = originalFunc.apply( this );
if (result instanceof Observable){
funcType = 'observable' ;
return result.pipe(
tap( val => {
cacheValue(val, now);
}));
} else if ( result instanceof Promise ){
funcType = 'promise' ;
return result
.then( value => {
cacheValue(value, now);
return value;
});
} else {
funcType = 'value' ;
cacheValue(result, now);
return result;
}
};
};
}
export class AppComponent {
callMethod1(){
console .log( this .method1());
}
callMethod2(){
this .method2().then( console .log);
}
callMethod3(){
this .method3().subscribe( console .log)
}
@Cache()
method1(){
return Math .random();
}
@Cache({
duration : 10000
})
method2(){
return new Promise ( resolve => {
setTimeout( () => resolve( Math .random()), 1000 );
});
}
@Cache()
method3(){
return of ( Math .random())
.pipe(debounceTime( 1000 ))
}
}
警告
当我们向方法添加参数时,即使参数更改,缓存的值也不会更改。 我们可能需要添加一种哈希方法来区分不同的调用,以便我们可以跳过缓存。
您可以在此处查看演示 。