可观察的开关/ flatMap立即触发

时间:2017-11-08 00:02:38

标签: facebook angular rxjs

我的服务中有以下代码:

public loginWithFacebook(): Observable<any> {
    console.log('Login');
    return Observable.fromPromise(this.fb.login()).flatMap((userData) => {
      return this.http.post(authFacebook, {access_token: userData.authResponse.accessToken}, { observe: 'response' });
    }).do( (response: HttpResponse<any>) => {
      const token = response.headers.get('x-auth-token');
      if (token) {
        localStorage.setItem('id_token', token);
      }
    });
  }

  getFacebookProfile():Observable<any> {
    console.log("Get Profile");
    return Observable.fromPromise(this.fb.getLoginStatus())
        .filter((state) => state.status === 'connected')
        .switchMapTo(Observable.fromPromise(this.fb.api('/me')));
  }

稍后我会在我的组件中使用它来获取登录成功后的个人资料信息。

this.profileData = this.usersService.loginWithFacebook()
    .flatMapTo(this.usersService.getFacebookProfile());

但是,由于某些原因,即使在登录过程完成之前,getFacebookProfile()也会立即触发。我收到了身份验证错误。此外,我必须登录两次才能显示个人资料信息。

我一直认为,一旦前一个发出一个值,switchMap和flatMap只会切换到下一个observable。 我在这里做错了什么?

- 编辑 -

如果我订阅了第一个Observable并在订阅中调用getFacebookProfile(),一切正常。但我觉得这不是一个非常优雅的解决方案。

2 个答案:

答案 0 :(得分:3)

问题在于承诺是渴望的。在撰写观察者时,您正在调用fromPromise,并且您将返回的承诺传递给loginWithFacebook

这意味着在调用subscribe时启动登录,而不是在它返回的observable上调用login时启动。

如果您希望在调用subscribe之前推迟public loginWithFacebook(): Observable<any> { console.log('Login'); return Observable.defer(() => Observable.fromPromise(this.fb.login())) .flatMap((userData) => { return this.http.post(authFacebook, { access_token: userData.authResponse.accessToken }, { observe: 'response' }); }) .do( (response: HttpResponse<any>) => { const token = response.headers.get('x-auth-token'); if (token) { localStorage.setItem('id_token', token); } }); } ,则可以使用$stateProvider

template <typename S>
S * findID([string or int] ID){
    for (typename vector<S*>::collectionsIter element = collection.begin() ; element != collection.end(); ++element)
        if((*element)->getID() == ID) return *element;
    return NULL;
}

有关使用可观察和承诺的更多信息,请参阅Ben Lesh的文章:defer

答案 1 :(得分:0)

最后感谢@ cartant的回答。但是,出于某种原因,我不得不用defer运算符包装它两次。如果有人能解释为什么有必要这样做,我将感激不尽。这有点奇怪。

public loginWithFacebook(): Observable<any> {
    return Observable.defer(() => 
        Observable.defer(() => this.fb.login()).flatMap((userData) => 
        {
            return this.http.post(authFacebook, {access_token: userData.authResponse.accessToken}, { observe: 'response' });
        }).do( (response: HttpResponse<any>) => {
            const token = response.headers.get('x-auth-token');
            if (token) {
                localStorage.setItem('id_token', token);
            }
        })
    );
  }

  getFacebookProfile():Observable<any> {
    return Observable.defer(() => 
        Observable.defer(() => this.fb.getLoginStatus())
        .filter((state) => state.status === 'connected')
        .switchMapTo(Observable.fromPromise(this.fb.api('/me')))
    );
  }