首页 文章

调用rxjs`Conject`的`next`取消http调用

提问于
浏览
1

我有一个应用程序在哪里注销用户,我必须点击一个REST endpoints . 在我的角度2前端,我有以下服务处理这个:

@Injectable()
export class AuthService {

  private authEvents: Subject<boolean>;

  constructor(private http: Http) {
    this.authEvents = new Subject<boolean>();
  }

  login(email: string, password: string): Observable<Response> {
    const url = 'rest/auth/login';
    const body = {
      email: email,
      password: password
    };
    return this.http.post(url, body)
      .do((res: Response) => {
        localStorage.setItem('currentUser', JSON.stringify(res.json()));
        this.authEvents.next(true);
      });
  }

  logout(): Observable<Response> {
    const url = 'rest/auth/logout';
    return this.http.post(url, {})
      .do((res: Response) => {
        localStorage.removeItem('currentUser');
        console.log('hereee!');
        this.authEvents.next(false);
      });
  }

  isSignedIn(): boolean {
    return localStorage.getItem('currentUser') !== null;
  }

  get events(): Observable<boolean> {
    return this.authEvents;
  }

}

让我解释一下我在这里要做的事情 . 我有一个 authEvents 主题,我在我的组件中订阅如下:

ngOnInit() {
  this.isSignedIn = this.authService.isSignedIn();
  this.authService.events.subscribe(() => {
    this.isSignedIn = this.authService.isSignedIn();
  });
}

当用户单击注销按钮时,我在此组件中调用以下函数:

logout() {
  this.authService.logout()
    .subscribe(() => {
      this.router.navigate(['/home']);
    }, e => this.handleError(e));
}

问题是即使注销调用尚未完成,浏览器也会重定向到home . 如此有效,有时候前端会成功退出,有时则不会!我在这做错了什么?

注意:自http呼叫发生以来,后端上的会话始终会注销 . 我还想看看如何使用Observables(和一般的rxjs)来实现这一点,尽管如果太麻烦,可以使用Promises .

2 回答

  • 2

    有了 Subject ,您就有这样的情况,即排放仅由订阅者接收,而不是由未来的订阅者接收 . 我的猜测是,这是你的问题 - 为了让你的生活更轻松,你可以使用 BehaviorSubject ,它也可以为任何未来的用户重新发布它的当前 Value ,所以你不必担心任何比赛 - 条件 .

    However: 严格地说它不再是一个事件,而是更多的一个状态,这是我建议你在你的情况下使用的 - 因为使用RxJS你可以利用状态的力量而不是事件 .

    @Injectable()
    export class AuthService {
    
      public currentUser$: BehaviorSubject<IUser> = new BehaviorSubject(null); // initial state is "null" => user is not logged in
      private isSignedIn$: Observable<boolean> = currentUser$.map(user => Boolean(user)).share();
    
      constructor(private http: Http) {
        this.authEvents = new Subject<boolean>();
      }
    
      login(email: string, password: string): Observable<Response> {
        const url = 'rest/auth/login';
        const body = {
          email: email,
          password: password
        };
        return this.http.post(url, body)
          .do((res: Response) => {
            localStorage.setItem('currentUser', JSON.stringify(res.json()));
            this.currentUser$.next(res.json()); // will automatically trigger the logged-in state as well 
          });
      }
    
      logout(): Observable<Response> {
        const url = 'rest/auth/logout';
        return this.http.post(url, {})
          .do((res: Response) => {
            localStorage.removeItem('currentUser');
            console.log('hereee!');
            this.currentUser$.next(null); // will automatically trigger the logged-in state as well 
          });
      }
    }
    

    在您的组件中

    private desotryed$: Subject<boolean> = new Subject();
    
    ngOnInit() {
      this.authService.isSignedIn$
        .takeUntil(this.desotryed$) // to prevent memory leaks through a perpetual subscription
        .subscribe(isSignedIn => {
          this.isSignedIn = isSignedIn;
        });
    }
    
    ngOnDestroy() {
      this.desotryed$.next(true);
    }
    

    由于RxJS5文档尚未包含BehaviorSubject,因此这里是旧文档的链接:https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md

  • 0

    这对我有用:

    正如@olsn建议的那样,我将 Subject 更改为 BehaviorSubject .

    但实际上在组件的html中出现了什么问题 .

    <li routerLinkActive="active"><a href="#" (click)="logout(); $event.preventDefault()">Logout</a></li>
    

    我必须在 (click) 事件中添加 $event.preventDefault() 调用,所有内容似乎都按预期工作 . 虽然有人可以向我解释为什么会这样,但这会很棒!

相关问题