Отписаться от Observables внутри сервисов?

Глядя на примеры кода, я часто вижу случай, когда наблюдаемые внутри сервисов не отписываются.

Вот пример:

export class AuthGuard implements CanActivate {

    private isLoggedIn: boolean;
    private isLoggedIn$: Observable<boolean>;


    constructor(private authService: AuthService, private router: Router) {
        this.isLoggedIn$ = this.authService.isLoggedIn();

        this.isLoggedIn$.subscribe(res => {
            if (res) {
                this.isLoggedIn = true;
            } 
            else {
                this.isLoggedIn = false;
            }
        });
    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        if (this.isLoggedIn) {
            return true;
        }
        else {
            this.router.navigate(['login']);
            return false;
        }     
    }
} 

Есть ли причина, по которой вы не отпишетесь от наблюдаемого this.isLoggedIn$ в этом случае? Или приведенный выше пример просто плохой код, приводящий к утечкам памяти?


person Michael    schedule 10.08.2018    source источник
comment
Возможный дубликат Нужно ли отписываться от наблюдаемых созданные методами HTTP?   -  person Vlad274    schedule 10.08.2018
comment
brianflove.com/2016/12/11/anguar-2-unsubscribe -observables пожалуйста, посетите эту статью, в любом случае это просто плохой пример кодирования   -  person Vaibhav Kumar Goyal    schedule 10.08.2018
comment
Я прочитал статью, прежде чем задавать вопрос .... статья ничего не касается вопроса о том, следует ли отписываться от наблюдаемых, даже если наблюдаемые содержатся не в Компоненте, а в Сервисе.   -  person Michael    schedule 10.08.2018
comment
@ Vlad274: Теперь, когда вам не нужно отписываться от наблюдаемых, созданных методами http ... но как насчет наблюдаемых, созданных Службами?   -  person Michael    schedule 10.08.2018


Ответы (2)


Глядя на примеры кода, я часто вижу случай, когда наблюдаемые внутри сервисов не отписываются.

Вы смотрите на ошибку.

Есть ли причина, по которой вы не отпишетесь от наблюдаемого this.isLoggedIn$ в этом случае?

Если вы хотите утечку памяти.

Или приведенный выше пример просто плохой код, приводящий к утечкам памяти?

Да, это утечка памяти. Функция подписки будет продолжать выполняться после того, как объект, на который ссылается this, будет уничтожен. Это может никогда не произойти, если этот объект никогда не будет уничтожен, но это пример кода, который не пройдет модульный тест, в котором объекты создаются временно.

Объекты, помеченные @Injectable(), часто ведут себя как синглтоны или имеют слабые ссылки. Это может работать какое-то время, но как только вы используете его во временном случае, произойдет утечка памяти.

person Reactgular    schedule 10.08.2018

Для базовых служб отмена подписки на самом деле не требуется, потому что служба существует до тех пор, пока живет приложение, а служба является одноэлементной. Проблема в том, что вы должны убедиться, что вы всегда используете его как синглтон (для лучших решений читайте ниже)

Для сервисов-компонентов отписку следует размещать внутри ngOnDestroy сервиса, потому что сервисы на самом деле могут реализовывать этот хук NgOnDestroy. Лучшим способом было бы использовать канал takeUntil(this.destroy) и испускать его при уничтожении.

Еще лучше было бы использовать канал async внутри шаблона и никогда напрямую не подписываться на подобные вещи.

С другой стороны, в вашей защите вы можете использовать трубу take(1), это приведет к первому выбросу и немедленной отмене подписки без необходимости для вас, это изменит ваш пример на этот. Как видите, подписки внутри кода нет:

export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.authService.isLoggedIn().pipe(
      take(1),
      tap((loggedIn) => {
        if (!loggedIn) {
          this.router.navigate(['login'])
        }
      })
    );    
  }
}

В итоге постарайтесь, чтобы ваше приложение было потоком Observables, используя асинхронные внутренние шаблоны и функцию канала rxjs вместо подписки и хранения дополнительных результатов. Чтобы isLoggedIn() выдавал последний результат при подписке, вы можете использовать канал shareReplay() или сделать его BehaviourSubject для начала.

person Poul Kruijt    schedule 10.08.2018
comment
Лично я считаю плохой практикой не отказываться от подписки, потому что она живет вечно. Все может измениться, и это детали реализации. Если вы когда-нибудь оцените всю жизнь, вы действительно не забудете пройти и исправить все это? - person Ingo Bürk; 10.08.2018
comment
@IngoBürk Это именно то, что я сказал в первом абзаце :) и более подробно рассказал об этом в остальной части ответа. - person Poul Kruijt; 10.08.2018