Reputation: 154
I develop an Angular 9 application and I don't understand how to use shareReplay
operator together with other operators. I did something like the following:
if (!this.cache[key]) {
this.cache[key] = this.http.get(...).pipe(
shareReplay(1),
flatMap(...),
map(...),
reduce(...)
);
}
return this.cache[key];
After this my application stucked with 100% CPU usage. When I changed it to:
if (!this.cache[key]) {
this.cache[key] = this.http.get(...).pipe(
flatMap(...),
map(...),
reduce(...),
shareReplay(1)
);
}
return this.cache[key];
it seems to work fine. Is it necessary to use the shareReplay
operator as the last one? Where this high CPU usage comes from?
Edit: a more detailed code snippet:
this.http.get(...).pipe(
// I would like to avoid several same http calls
shareReplay(1),
// I would like to flatten an array of objects that comes from backend
flatMap(option => option),
// I need to map all objects to a format that is acceptable by some library
map(option => ({
value: option.key,
label: option.value
})),
// I must reduce it back to an array of the new objects
reduce((acc: { value: string; label: string }[], option) => {
acc.push(option);
return acc;
}, [])
);
Upvotes: 3
Views: 1328
Reputation: 14199
The position of the share
operator in the pipe is important, because it determines how many subscribers will subscribe to operators that come before share
.
Take the following example:
const interval$ = interval(1000).pipe(
tap(() => console.log("Interval Triggered")
);
interval$.subscribe();
interval$.subscribe();
What you'll see is that we get two console message every second. That makes sense, because in RxJS, every subscriber ("consumer") will cause the factory to create a new "producer" (here a setInterval
). In this case - there is no share
operator - every subscriber will go up the operator chain and subscribe to the previous operator - until we subscribe to interval
twice.
Now, let's take a look at share
:
const interval$ = interval(1000).pipe(
tap(() => console.log("Interval Triggered"),
share()
);
interval$.subscribe();
interval$.subscribe();
With this version, we'll only get one message per second. Why?
Because share
won't subscribe to it's source as long as there is one active subscriber. So, we subscribe to share()
twice, but share()
only subscribes to interval
once.
If we switch positions, we get a different result:
const interval$ = interval(1000).pipe(
share(),
tap(() => console.log("Interval Triggered"),
);
interval$.subscribe();
interval$.subscribe();
In this case, we subscribe to tap
twice - and those two tap
operators subscribe to share
twice. That's why we get two console messages per second.
As a rule of thumb, you'll most likely want your share
at the end of your pipe, as this will cause the operators that come before share
to be subscribed to only once.
To explain your CPU consumption: I suspect that you subscribe very often to this Observable - with share
not being the last operator, this means that you'll resubscribe to flatMap
etc. over and over again.
Upvotes: 7