RxJS many Subjects with few Subscriptions vs Single Subject with many Subscriptions

I'm creating an Angular application which sends and receives data over a websocket connection. I have one service SocketService which opens and keeps the connection, that service is injected into other services.

Each message on the websocket looks like {ctrl: string, payload: any} and a Subject is created for each controller (ctrl) in the SocketService, to which other services can subscribe, so each service subscribes only to the messages intended for it.

With an increasing amout of services and therefor controllers I have an increasing amount of Subjects in the SocketService and now I was wondering if it was more efficent to let each service subscribe to the websocket messages directly and have the service itself check if the message was intended for it or not. But that would mean that each message on the socket triggers lots of subscribers.

// in the socketservice
private createSocketSubscription(): void {
  // Snipped from the socket service when receiving messages on the socket connection
  this._subscription = this._subject.pipe(map(msg => JSON.parse(msg.data))).subscribe((data) => {
    const ctrl = data[this.CTRL] || 'common';
    this._registry[ctrl].next(data[this.PAYLOAD]);
  }
}

public registerController(ctrl: string, def?: any) {
  if (!(this._registry[ctrl]))
    this._registry[ctrl] = new BehaviorSubject(def?def:{});
  return this._registry[ctrl].asObservable();
}


// in some service
constructor(private socketService: SocketService) {
  this.socketService.registerController('something').subscribe(payload=> {
    // do stuff with the payload
  });
}

versus

// in some service
constructor(private socketService: SocketService) {
  this.socketService.getSubject().subscribe(data => {
    if(data['ctrl'] === 'something') {
      // do stuff with the payload
    }
  });
}

Which approach is more efficient or does it even make a difference? I left out stopping and unsubscribing processes, I only wonder if it is better to have few subscriptions to many subjects or many subscriptions to a single subject?

1 answer

  • answered 2020-06-02 18:01 satanTime

    Subscription if should be a final destination, anything else should be a pipe. In angular apps the best way is to avoid subscriptions at all and to use async pipe in templates when you need data from a stream.

    In your example

    .subscribe(() => {
        .next
      }
    

    means that one stream should trigger another stream, the right way to do it is to use mergeMap, switchMap etc, when .next joins the .subscribe stream and handles its emits.

    instead of ctrl map you could use filter operator

    this.socketService.getSubject().pipe(
      filter(data => data['ctrl'] === 'something'),
    ).subscribe(data => {
      // do stuff with the payload
    });
    

    The way I would recommend

    // in some service
    public data$: Observable<any>;
    
    constructor(private socketService: SocketService) {
      this.data$ = this.socketService.getSubject().pipe(
         filter(data => data['ctrl'] === 'something'),
         // map(data => data), // do something
      );
    }
    

    and in the template

    <ng-container *ngIf="data$ | async as data">
      {{ data | json }}
    </ng-container>