简体   繁体   中英

In rxjs, how to unsubscribe from "inner" "interval" observables using takeUntil or takeWhile

[EDIT 11/5] After testing with some of the suggested solutions, I noticed some of my rxjs imports were wrong (I was importing from 'rxjs/internal' iso from 'rxjs'). So if you come across an error like this, that's something that you might have a look at.

There already exist a couple of questions on Stackoverflow related to unsubscribing from an observable, though none of them are very helpful to my specific problem.

In my ("chat") application, I can search for users by typing their name in a search box. After fetching the users, I want to display whether these users are online or not.

Here's my approach.

There's a BehaviorSubject for capturing the search term:

private term$ = new BehaviorSubject(null);

Whenever there's a new value on the term$ observable, I start looking for new users:

users$: Observable<User[]> = this.term$.pipe(
    filter(term => !!term),
    switchMap(term => this.usersService.searchUsers(term))
);

But I also want to periodically check whether these users are "online", hence I change above Observable like so (note that last line):

users$: Observable<any[]> = this.term$.pipe(
    filter(term => !!term),
    switchMap(term => this.usersService.searchUsers(term)),
    switchMap(users => zip(...users.map(user => this.checkLoggedInF(user))))
);

For all users I create an "Interval" observable. checkedLoggedInF is a function that checks every 5 seconds with the server whether the given user is online:

checkLoggedInF = user => {
    return interval(5000).pipe(
      switchMap(() => this.usersService.isLoggedIn(user.loginnaam).pipe(
        map(loggedIn => ({...user, loggedIn}))
      ))
    );
}

Now the problem is that whenever there is a new search term (on the term$ observable), the "checkLoggedIn interval" observables should be unsubscribed to. I have tried using a takeUntil operator in both the "checkLoggedIn interval" observable and the parent "users$" observable, but to no avail. Also using a takeWhile operator was ineffective.

try to use .tap() before switchMap where you are calling checkLoggedInF , and save your observable from checkLoggedInF in a property

checkLoggedInF = user => {
    this.checkLoggedInF$ = interval(5000).pipe(
      switchMap(() => this.usersService.isLoggedIn(user.loginnaam).pipe(
        map(loggedIn => ({...user, loggedIn}))
      ))
    );
    return this.checkLoggedInF$;
}

users$: Observable<any[]> = this.term$.pipe(
    filter(term => !!term),
    switchMap(term => this.usersService.searchUsers(term)),
    tap(() => { this.checkLoggedInF$ && this.checkLoggedInF$.unsubscribe() })
    switchMap(users => zip(...users.map(user => this.checkLoggedInF(user))))
);

or another option you can check and unsubscribe() in your checkLoggedInF too

checkLoggedInF = user => {
   this.checkLoggedInF$ && this.checkLoggedInF$.unsubscribe() 
   this.checkLoggedInF$ = interval(5000).pipe(
     switchMap(() => this.usersService.isLoggedIn(user.loginnaam).pipe(
       map(loggedIn => ({...user, loggedIn}))
     ))
   );
   return this.checkLoggedInF$;
}

In the end it was a stupid mistake on my side, some of my 'rxjs imports' were wrong (I was importing from rxjs/internal). When I fixed the imports, switchMap functions as expected (closing the subscription when the "upper stream" changes).

So in short: don't import from 'rxjs/internal'

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