0
votes

In my angular app, I have a state stored in a service as a BehaviorSubject, when data comes from server I call the next function to update data and to notify all subscribers.

PseudoCode:

   ajaxCall_1 -> updateData(response2)
   ajaxCall_2 -> updateData(response2)
   ajaxCall_3 -> updateData(response3)
   ...
    
private data$:Observable<any>;
public updateData(newData){
var currentData = data$.value;
data$.next({...currentData , ...newData})
}

Since a lot of components subscribed to that BehaviorSubject, I think it can cause some performance issue when my app is loading: on load there's a lot of notifications from server and each time next function is called components are rendering.

Do you think if I delayed the call to the next after something like 500 ms I will see performance improvement? What will be the rxjs way to delay the next call?

2
Do u need all calls? Seems like it is updating frequently. Then u need to store final state there? then u can use switchmap - PushpikaWan
Yes I need all calls. How switchmap can help me there? - IsraGab
No SwitchMap only keep the latest call If u need all calls, need to use MergeMap there. What is the purpose of catches all the call. Actually have no idea about ur scenario. If so u might need to change the structure here. Maybe use the queue to enqueue states and dequeue thing like that - PushpikaWan
I added some pseudocode, maybe clearer now? - IsraGab
Using unsubscribe maybe help you - Hasan Fathi

2 Answers

0
votes

A performance impact will be from the components that subscribe to that BehaviorSubject rather than itself.

Anyway you can use the delay operator to delay the next call, something like this:

private sub$ = new BehaviorSubject<any>(null);
public data$ = this.sub$.pipe(delay(500)));

public updateData(newData){
  var currentData = sub$.value;
  sub$.next({...currentData , ...newData})
}
0
votes

Here is a way you can decide how often (say once per second), your updateData function is called.

What I've done here is buffer all your calls for a second, then reduce the buffer into a single object, then emit that object to updateData.

merge(
  ajaxCall_1,
  ajaxCall_2,
  ajaxCall_3,
  ...
).pipe(
  bufferTime(1000),
  filter(buffer => buffer.length > 0),
  map(bufferedNewData => bufferedNewData.reduce(
    (acc, curr) => ({...acc, ... curr}), {}
  ))
).subscribe(updateData);

On the other hand, it might make sense to display nothing until the first set of calls all complete. This might make the initial load more predictable. That's only a slight change:

forkJoin([
  ajaxCall_1,
  ajaxCall_2,
  ajaxCall_3,
  ...
]).pipe(
  map(loadNewData => loadNewData.reduce(
    (acc, curr) => ({...acc, ... curr}), {}
  ))
).subscribe(updateData);