Yassine Badache
Yassine Badache

Reputation: 1851

Potential issue with subscription on event with Angular

Can subscribing to a service during an event (e.g with clicking a button) bring issue when done without a Subscription object ? For instance, here is a small snippet I stumbled upon:

usernames: string[];

...

public getFullName(userID: string)  {
    this.restApi.getFullName(userID).subscribe(response => {
        this.usernames.push(response);
    });
}

I was wondering if a subscription was "added" for each call to the service ? (the first call adds a subscription, which pushes one item, then a second adds another subscription to this Observable and pushes an item twice, and so on and so on)

It is a better practice to subscribe to every services in an OnInit() method, so that you subscribe only once without memory / performance issues ? Or use a Subscription object to make sure that your subscription is "overrid" when called a second time ?

Upvotes: 1

Views: 205

Answers (3)

StPaulis
StPaulis

Reputation: 2926

The less subscriptions you have, the better chances that your app will work without any memory issue.

The code above is dangerous enough because you subscribe for every event and you never unsubscribe.

In other words, If you need to create a subscription on every click you can support it but If you could just create one subscription on initialized it would more efficient.

I would suggest you to read this article: when-to-unsubscribe and this one: how-to-unsubscribe

#Edit

I really like the code of the other answer here by @MoxxiManagarm, just leaving my two words because I really believe that this articles are helpful If someone ends up reading this question.

Upvotes: 1

Jacopo Sciampi
Jacopo Sciampi

Reputation: 3433

Everytime getFullName a new subscription is added, so yes; Performances will suffer for this. You could use a scanner as MoxxiManagarm said or you can simplify the logic using takeWhile:

public getFullName(userID: string)  {
    let isAlive = true;
    this.restApi.getFullName(userID).pipe(
    takeWhile(() => isAlive)
    ).subscribe(response => {
        isAlive = false;
        this.usernames.push(response);
    });
}

The takeWhile operator closes the subscripion and removes it when a certain condition is reached.

Upvotes: 1

MoxxiManagarm
MoxxiManagarm

Reputation: 9134

Your doubts are true. I recommend the scan operator for your usecase.

lastUserIdRequested: Subject<string> = new Subject();
usernames: string[];

ngOnInit() {
  this.lastUserIdRequested.pipe(
    concatMap(userId => this.restApi.getFullName(userId)),
    scan((acc, fullname) => [...acc, fullname], []),
  ).subscribe(fullnames => this.usernames = fullnames);
}

public getFullName(userId: string) {
  // this could be called directly from template without the getFullName method
  this.lastUserIdRequested.next(userId);
}

Upvotes: 1

Related Questions