简体   繁体   中英

Angular 2: BehaviorSubject, avoid with multiple subscription

Hi I have following problem in my Angular 2 app.

I have BroadcastService :

@Injectable()
export class BroadcastService {

   private broadcastSubject: BehaviorSubject<Event> = new BehaviorSubject<Event>(0);

   public next(event: Event): void {
     return this.broadcastSubject.next(event);
   }

   public subject(event: Event): Observable<Event> {
     return this.broadcastSubject.asObservable().filter(e => e === event);
   }
 }

Which I use in components like this:

export class MyComponent implements OnInit {

   constructor(public broadcastService: BroadcastService) {
     this.broadcastService.subject(Event.BLA_EVENT).subscribe(() => this.bla());
   }
...

Whenever I route to '/mycomponent' the MyComponent.constructor is called, so the Event.BLA_EVENT is subscriped multiple times.

Any advice how to prevent multiple time subscription?

Manual subscriptions inside components should always be unsubscribed in the ngOnDestroy hook. This is to prevent memory leaks, unwanted double subscriptions, and errors occuring if you change something inside the component after it has been destroyed:

export class MyComponent implements OnInit, OnDestroy {

   private _broadSub;

   constructor(public broadcastService: BroadcastService) {}

   ngOnInit() {
      this._broadSub = this.broadcastService.subject(Event.BLA_EVENT).subscribe(() => this.bla());
   }

   ngOnDestroy() {
       this._broadSub.unsubscribe();
   }

}

I had a similar problem and I solved it by checking the quantity of the observers on the event. The option using ngOnDestroy may be the best, but another approach could be something like:

constructor(public broadcastService: BroadcastService) {
    if(this.broadcastService.subject(Event.BLA_EVENT).observers.length == 0) {
        this.broadcastService.subject(Event.BLA_EVENT).subscribe(() => this.bla());
    }
}

Now you can use BroadcastChannel in almost all browsers (see caniuse):

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class BroadcastService {
  private consumer: BroadcastChannel;
  private producer: BroadcastChannel;

  constructor() {
    this.consumer = new BroadcastChannel('pluginGlobalEventBus');
    this.producer = new BroadcastChannel('pluginGlobalEventBus');
  }

  postMessage(message: any): void {
    this.producer.postMessage(message);
  }

  addEventListener(eventName, listener): void {
    this.consumer.addEventListener('message', event => {
      if (event.data.name === eventName) {
        listener(event.data.value);
      }
    });
  }
}

export class EmitEvent {
  constructor(public name: any, public value?: any) {}
}

usage:

// sending
const event = new EmitEvent('logEvent', 'BUTTON_CLICKED');
this.broadcastService.postMessage(event);


// listening
this.broadcastService.addEventListener('logEvent', (interaction: string) => {
  console.log(interaction)
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM