英文:
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 '@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']);
}));
}
}
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()
,它将自动取消订阅返回的可观察对象。
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.
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()); // <----------- Here!
});
});
return of (canActivateObservables).pipe(prioritizedGuardValue());
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论