【问题标题】:Observable switch/flatMap fires instantlyObservable switch/flatMap 立即触发
【发布时间】:2017-11-08 00:02:37
【问题描述】:

我的服务中有以下代码:

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() ,一切正常。但我觉得这不是很优雅的解决方案。

【问题讨论】:

    标签: facebook angular rxjs


    【解决方案1】:

    问题是承诺是急切的。当您编写 observable 并将返回的承诺传递给 fromPromise 时,您正在调用 this.fb.login()。

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

    如果您希望 login 被延迟到 subscribe 被调用,您可以使用 defer:

    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);
          }
        });
    }
    

    有关使用 observables 和 Promise 的更多信息,请参阅 Ben Lesh 的文章:RxJS Observable interop with Promises and Async-Await

    【讨论】:

    • 听起来应该可以。但是当我刚刚尝试时,我得到了相同的结果。由于某种原因仍然会立即触发..
    • 在编写可观察链时,您需要在所有传递承诺的位置使用defer。
    【解决方案2】:

    感谢@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')))
        );
      }
    

    【讨论】:

      猜你喜欢
      • 2021-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多