Reactive Caching of HTTP Service

前端 未结 3 802
后悔当初
后悔当初 2021-02-12 13:01

I am using RsJS 5 (5.0.1) to cache in Angular 2. It works well.

The meat of the caching function is:

const observable = Observable.defer(
    () => a         


        
3条回答
  •  失恋的感觉
    2021-02-12 13:49

    Updated answer:

    If always want to use the previous value while a new request is being made then can put another subject in the chain which keeps the most recent value.

    You can then repeat the value so it is possible to tell if it came from the cache or not. The subscriber can then filter out the cached values if they are not interested in those.

    // Take values while they pass the predicate, then return one more
    // i.e also return the first value which returned false
    const takeWhileInclusive = predicate => src =>
      src
      .flatMap(v => Observable.from([v, v]))
      .takeWhile((v, index) =>
         index % 2 === 0 ? true : predicate(v, index)
      )
      .filter((v, index) => index % 2 !== 1);
    
    // Source observable will still push its values into the subject
    // even after the subscriber unsubscribes
    const keepHot = subject => src =>
      Observable.create(subscriber => {
        src.subscribe(subject);
    
        return subject.subscribe(subscriber);
      });
    
    const cachedRequest = request
       // Subjects below only store the most recent value
       // so make sure most recent is marked as 'fromCache'
      .flatMap(v => Observable.from([
         {fromCache: false, value: v},
         {fromCache: true, value: v}
       ]))
       // Never complete subject
      .concat(Observable.never())
       // backup cache while new request is in progress
      .let(keepHot(new ReplaySubject(1)))
       // main cache with expiry time
      .let(keepHot(new ReplaySubject(1, this.RECACHE_INTERVAL)))
      .publish()
      .refCount()
      .let(takeWhileInclusive(v => v.fromCache));
    
      // Cache will be re-filled by request when there is another subscription after RECACHE_INTERVAL
      // Subscribers will get the most recent cached value first then an updated value
    

    https://acutmore.jsbin.com/kekevib/8/edit?js,console

    Original answer:

    Instead of setting a window size on the replaySubject - you could change the source observable to repeat after a delay.

    const observable = Observable.defer(
        () => actualFn().do(() => this.console.log('CACHE MISS', cacheKey))
      )
      .repeatWhen(_ => _.delay(this.RECACHE_INTERVAL))
      .publishReplay(1)
      .refCount()
      .take(1)
      .do(() => this.console.log('CACHE HIT', cacheKey));
    

    The repeatWhen operator requires RxJs-beta12 or higher https://github.com/ReactiveX/rxjs/blob/master/CHANGELOG.md#500-beta12-2016-09-09

提交回复
热议问题