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

时间:2017-02-21 07:42:18

标签: angular typescript rxjs

我有一个应用程序在哪里注销用户,我必须点击一个REST端点。在我的角度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 个答案:

答案 0 :(得分:2)

根据Subject您的情况,只有订阅者才会收到排放,而不是未来的订阅者。我的猜测是这是你的问题 - 为了让你的生活更轻松,你可以使用BehaviorSubject,它可以为任何未来的订阅者重新发布它的当前价值,所以你不必担心关于任何比赛条件。

但是:严格来说,它不再是一个事件,而是一个更多的状态,这是我建议你在你的情况下使用的 - 因为使用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

答案 1 :(得分:0)

这对我有用:

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

但似乎出错的是组件的html。

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

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