缓存可观察对象/承诺

有时,我们可能只想缓存某个方法的返回值一段时间。 打字稿装饰器非常方便。 我们可以创建一个自定义装饰器,并将其放置在方法上并使用它来完成。

  @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 ))
  }
}

警告

当我们向方法添加参数时,即使参数更改,缓存的值也不会更改。 我们可能需要添加一种哈希方法来区分不同的调用,以便我们可以跳过缓存。

您可以在此处查看演示

From: https://hackernoon.com/caching-observables-85273795

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值