如果在守卫的 canActivate 中不取消订阅 Observable 会发生什么?

huangapple go评论96阅读模式
英文:

What may happen if you don't unsubscribe from an Observable in canActivate of a guard?

问题

请看下面的示例:

import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable, of } from 'rxjs';
import { Injectable } from '@angular/core';
import { AuthService } from './auth.service';
import { map, take } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> | Observable<boolean | UrlTree> | boolean {
    return this.authService.user.pipe(
      take(1),
      map(user => {
        if (!!user) {
          return true;
        }
        return this.router.createUrlTree(['/auth']);
      })
    );
  }
}

user 的类型是 BehaviorSubject,它定义了用户的授权状态,如果它等于一个对象,则用户已登录,否则没有。因此,如果用户已登录,该守卫会激活 /auth 路由,否则会重定向到登录页面。
现在,如果我从管道中删除 take(1),以便不取消订阅会发生什么?

英文:

Please, see an example below

import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from &#39;@angular/router&#39;;
import {Observable, of} from &#39;rxjs&#39;;
import {Injectable} from &#39;@angular/core&#39;;
import {AuthService} from &#39;./auth.service&#39;;
import {map, take} from &#39;rxjs/operators&#39;;

@Injectable({providedIn: &#39;root&#39;})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService,
              private router: Router) {
  }
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise&lt;boolean&gt; | Observable&lt;boolean | UrlTree&gt; | boolean {
    return this.authService.user.pipe(
      take(1),
      map(user =&gt; {
        if (!!user) {
          return true;
        }
        return this.router.createUrlTree([&#39;/auth&#39;]);
      }));
  }
}

The user is of type BehaviorSubject, and it defines an authorization status of an user, that is if it is equal to an object, then an user is logged in, and not otherwise. Therefore, the guard activates /auth route if the user is logged in, and it redirects us otherwise.
Now, what will happen if I remove take(1) from the pipeline so that we do not unsubscribe as a result?

答案1

得分: 3

什么都不会发生,您所经历的相同行为将被保留。我猜这个问题更多地涉及到不取消订阅的“成本”。

在底层,Angular将first操作符添加到可观察对象中,在发出第一个值后完成可观察对象。

这是在这个PR中添加的。

英文:

Nothing will happen, the same behaviour you experience will be preserved. I guess this question is angled more towards the "cost" of not unsubscribing.

Under the hood, angular adds the first operator to the observable, which completes the observable after emitting the first value.

This was added on this PR

答案2

得分: 2

我认为没有什么需要担心。
就我所见,每次运行canActivate守卫时,通过使用first(),它将自动取消订阅返回的可观察对象。

check_guard.ts

function runCanActivate(
    futureRSS: RouterStateSnapshot, futureARS: ActivatedRouteSnapshot,
    moduleInjector: Injector): Observable<boolean|UrlTree> {
  const canActivate = futureARS.routeConfig ? futureARS.routeConfig.canActivate : null;
  if (!canActivate || canActivate.length === 0) return of (true);

  const canActivateObservables = canActivate.map((c: any) => {
    return defer(() => {
      const guard = getToken(c, futureARS, moduleInjector);
      let observable;
      if (isCanActivate(guard)) {
        observable = wrapIntoObservable(guard.canActivate(futureARS, futureRSS));
      } else if (isFunction<CanActivateFn>(guard)) {
        observable = wrapIntoObservable(guard(futureARS, futureRSS));
      } else {
        throw new Error('Invalid CanActivate guard');
      }
      return observable.pipe(first()); // <----------- 这里!
    });
  });
  return of (canActivateObservables).pipe(prioritizedGuardValue());
}
英文:

I think there is nothing to worry about.
As far as I can see, every time a canActivate guard is run, by using first(), it will automatically unsubscribe from the returned observable.

check_guard.ts

function runCanActivate(
    futureRSS: RouterStateSnapshot, futureARS: ActivatedRouteSnapshot,
    moduleInjector: Injector): Observable&lt;boolean|UrlTree&gt; {
  const canActivate = futureARS.routeConfig ? futureARS.routeConfig.canActivate : null;
  if (!canActivate || canActivate.length === 0) return of (true);

  const canActivateObservables = canActivate.map((c: any) =&gt; {
    return defer(() =&gt; {
      const guard = getToken(c, futureARS, moduleInjector);
      let observable;
      if (isCanActivate(guard)) {
        observable = wrapIntoObservable(guard.canActivate(futureARS, futureRSS));
      } else if (isFunction&lt;CanActivateFn&gt;(guard)) {
        observable = wrapIntoObservable(guard(futureARS, futureRSS));
      } else {
        throw new Error(&#39;Invalid CanActivate guard&#39;);
      }
      return observable.pipe(first()); // &lt;----------- Here!
    });
  });
  return of (canActivateObservables).pipe(prioritizedGuardValue());
}

huangapple
  • 本文由 发表于 2020年1月6日 18:35:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/59610546.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定