Search code examples
rxjsreactive-programmingrxjs6

fromFetch operator does not reset cache on unsubscribe


I will provide two version of code snippet for better understaing:

1st one

const test = timer(0, 1000).pipe(shareReplay({ bufferSize: 1, refCount: true }));

const subscription1 = test.subscribe(
  (value) => console.log('Subscription 1:', value),
  (error) => console.error(error),
  () => console.log('Subscription 1 completed'),
);

setTimeout(() => {
  subscription1.unsubscribe();
}, 6000);

setTimeout(() => {
  const subscription2 = test.subscribe(
    (value) => console.log('Subscription 2:', value),
    (error) => console.error(error),
    () => console.log('Subscription 2 completed'),
  );
}, 10000);

Main point of this code is to test how shareReply works with refCount set to true. Behaviour is expected, since
subscription1 observable emits 5 times, then I unsubscribe, meaning refCount will be 0 again for test, what causes test observable to drop the cache. When i subscribe with subscription2 the emission start with 0.

But if i use fromFetch operator, cache of test observable is not dropped, even if there are no active subscribers to test observable:

export const groupsRaw$ = fromFetch('/some_endpoint').pipe(
  shareReplay({ bufferSize: 1, refCount: true }),
);

const subscription1 = groupsRaw$.subscribe(
  (value) => console.log('Subscription 1:', value),
  (error) => console.error(error),
  () => console.log('Subscription 1 completed'),
);

setTimeout(() => {
  subscription1.unsubscribe();
}, 6000);

setTimeout(() => {
  const subscription2 = groupsRaw$.subscribe(
    (value) => console.log('Subscription 2:', value),
    (error) => console.error(error),
    () => console.log('Subscription 2 completed'),
  );
}, 10000);

There will be only one request in the network tab.

I expect that subscription2 on subscribe should refetch data, but it takes it from cache. But cache should be already dropped. What do i miss?


Solution

  • The behavior you are seeing doesn't have to do with using fromFetch vs timer, it has to do with the source completing vs not completing.

    shareReplay will cache the source observable for future subscribers if its source completes. (see this github issue for reference).

    To achieve your desired behavior, you can use share and specify resetOnComplete = true:

      share({
        connector: () => new ReplaySubject(1),
        resetOnComplete     : true,
        resetOnRefCountZero : true,
      })
    

    Here's a little StackBlitz you can play around with.


    Since all the "reset" options are true by default, you don't actually need to specify them. So you could simply do:

      share({ connector: () => new ReplaySubject(1) })