Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJS cache and refresh with shareReplay

I am using a caching for some data retrieved from an API, for logical reasons the stored data is valid only for a limited time, so I am making use of something like:

someApiData$ = this.getData()
    .pipe(shareReplay(1, 3000))

What seems to be obvious to me but apparently is not to the creator of the shareReplay operator is that if the data is no longer cached it should be re-fetched, or at the very least I should have another parameter that will give me this option, something like:

someApiData$ = this.getData()
    .pipe(shareReplay(1, 3000, shouldRefresh))

Instead, what the next subscriber will get is null. So, I am looking for an elegant solution to this issue.

like image 552
Ravid Goldenberg Avatar asked Jun 29 '20 07:06

Ravid Goldenberg


1 Answers

After some trails with the answers on this thread and some other approaches on the web, this is what I ended up with. It gives the ability to:

  1. Cache values
  2. Refresh the values automatically if the data is no longer cached
  3. Work directly with the Observable
  4. Specify the duration of the cache lifetime if needed
  5. Unclutter my services and provide a reusable solution

My caching util:

export class SharedReplayRefresh {

    private sharedReplay$: Observable<T>;
    private subscriptionTime: number;


   sharedReplayTimerRefresh(
       source: Observable<T>, bufferSize: number = 1,
       windowTime: number = 3000000,  scheduler?: SchedulerLike): Observable<T> {

        const currentTime = new Date().getTime();
        if (!this.sharedReplay$ || 
            currentTime - this.subscriptionTime > windowTime) {
            this.sharedReplay$ = source.pipe(shareReplay(
                bufferSize, windowTime, scheduler));
            this.subscriptionTime = currentTime;
        }

        return this.sharedReplay$;
    }
}

My data-service:

export class DataService {
    
    constructor(private httpClient: HttpClient) { }

    private dataSource = 
        new SharedReplayRefresh<Data>();
    private source = this.httpClient.get<Data>(url);
    
    get data$(): Observable<Data> {
        return this.dataSource .sharedReplayTimerRefresh(this.source, 1, 1500);
    }
}
like image 140
Ravid Goldenberg Avatar answered Nov 11 '22 07:11

Ravid Goldenberg