WSD
WSD

Reputation: 3587

Practical differences between explicitly subscribing to an Observable vs calling subscribe() with parameter

I have a service that returns an Observable:

contactsSerice (): Observable<Contact[]> {...}

For using this data on my template and keep the template "alive" based on what's in the Observable I've found two ways.

I declared a Subject within the context of my Component:

export class ExampleComponent implements OnInit {

    contacts$: Subject<Contact[]> = new Subject<Contact[]>();
    constructor(){}

}

And then:

  1. Explicitly subscribing to the Observable (and emitting the contacts) like this:
ngOnInit() {

   this.contactsSerice().subscribe((contacts: Contact[]) => {
           this.contacts$.next(contacts);
   });
}
  1. Calling directly the .subscribe() using my Subject as a parameter:
ngOnInit() {
  this.contactsSerice().subscribe(this.contacts$);
}

After this, I can use the | async pipe in the template like:

<section *ngIf="contacts$ | async as contacts">
...
</section>

I was wondering if there's any practical difference between calling subscribe() passing the Subject vs properly subscribing to the Observable and trigger the Subject's next() method.

Upvotes: 2

Views: 508

Answers (2)

ChrisY
ChrisY

Reputation: 1783

There is an actual difference:

When we are subscribing via a Subject then error and complete notifications are passed to it. So if our source observable is completing, then the subject is completing as well. This might be an unwanted behaviour, because a completed Subject does ignore all further notifications.

 /*
 Variant 1:
   This works only once:
     - The complete notification from the HTTP call is forwarded to the subscribing subject
     - A completed subject ignores further values
 */
 this.inventoryService.loadBeers(page).subscribe(this.beers$);


 // could be thought of the same as: 
 this.inventoryService.loadBeers()
   .subscribe(
     (response) => this.beers$.next(response), 
     (error) => this.beers$.error(error), 
     () => this.beers$.complete()
 );

 /*  
  Variant 2: Here we don't pass the completed event to the beers$ 
 */
 this.inventoryService.loadBeers()
   .subscribe(
     (response) => this.beers$.next(response), 
     (error) => console.log('Errors are not passed to beers$ subject'), 
     () => console.log('Complete notification is not passed to beers$ subject')
 );

You can also take a look at the stackblitz example where I tried to show this behaviour.

Upvotes: 3

The short answers is none, there is no difference (besides that you will be using a bit more memory for the Subject itself).

From a practical point of view (let's say the question is when should I use intermediate Subject) you want to use intermediate subjects whenever you want to cache somehow the response that you receive from your back-end.

Let's say for example you want to do the following thing

// Left side of the screen
<section *ngIf="contacts$ | async as contacts">
...
</section>

// Right side of the screen
<section *ngIf="contacts$ | async as contacts">
...
</section>

So in this case, if you are using directly the service you will make two calls to the back-end (as each |async is a new subscription).

So the short answer is that there will be no difference, but it is better to have it.

** Side note: If you are new to Angular and have trouble managing states, look into NGRX.

Upvotes: -1

Related Questions