nest
nest

Reputation: 1395

Rxjs shareReplay with invalidation (timeout)

In Rxjs imagine a stream that fetches some data and then uses shareReplay to hold the latest value for optimization purposes.

const value = fetchData().pipe(
  shareReplay(1)
)

But what if this value can expire in which case (and only in that case) it should be re-fetched.

const value = fetchData().pipe(
  shareReplay(1),
  switchMap((value) => isExpired(value) ? fetchData() : of(value))
)

This isn't quite right for after the first re-fetch the value is no longer shared and would be re-fetched every time.

How could this functionality of "hold latest value" and "invalidation" be expressed in Rxjs?

Upvotes: 2

Views: 1410

Answers (2)

maxime1992
maxime1992

Reputation: 23813

You could do the following:

const value$ = fetchData().pipe(
  expand(res => timer(res.validForMs).pipe(switchMap(() => fetchData()))),
  shareReplay(1)
);

This way, the cache invalidation happens before the shareReplay and every subscriber will also get latest value.

I've made a simple example to demonstrate:

//  just mocks
// ---------------------------
const fetchData = () =>
  of({
    token: uuid(),
    validForMs: 2000
  });
// ---------------------------

const value$ = fetchData().pipe(
  expand(res => timer(res.validForMs).pipe(switchMap(() => fetchData()))),
  shareReplay(1)
);

merge(
  value$.pipe(
    tap(res => console.log(`Subscriber 1 received token ${res.token}`))
  ),
  value$.pipe(
    tap(res => console.log(`Subscriber 2 received token ${res.token}`))
  ),
  value$.pipe(
    tap(res => console.log(`Subscriber 3 received token ${res.token}`))
  )
).subscribe();

And the output is:

Subscriber 1 received token f7facb6f-2c16-4b0f-abc7-53e5c7461778
Subscriber 2 received token f7facb6f-2c16-4b0f-abc7-53e5c7461778
Subscriber 3 received token f7facb6f-2c16-4b0f-abc7-53e5c7461778

Subscriber 1 received token 67419f6a-65ff-482d-9ede-c38b1405e31e
Subscriber 2 received token 67419f6a-65ff-482d-9ede-c38b1405e31e
Subscriber 3 received token 67419f6a-65ff-482d-9ede-c38b1405e31e

Subscriber 1 received token b978b439-3ae5-440e-9d96-9f320db6e051
Subscriber 2 received token b978b439-3ae5-440e-9d96-9f320db6e051
Subscriber 3 received token b978b439-3ae5-440e-9d96-9f320db6e051

Demo here https://stackblitz.com/edit/rxjs-kqybmd

Upvotes: 2

Zazaeil
Zazaeil

Reputation: 4104

Well, just keep a variable in place: const val = fetchData().pipe(shareReplay(1)); Then: val.pipe(switchMap((value) => isExpired(value) ? val : of(value))).

Upvotes: 0

Related Questions