有人可以解释为什么 BehaviorSubject 结果是这样的吗?

Can someone explain that why the BehaviorSubject result like this?

我编写了一个服务,其中包含一个名为 loginStatus 的 BehaviorSubject 和一个 return 函数。

private loginStatus = new BehaviorSubject<boolean>(false);

getLoginStatus(): Observable<boolean> {
  return this.loginStatus;
}

然后我在其他组件的 DOM 中使用 4 个不同 div 中的异步管道订阅它,我只是评论了 shareReplay 以显示我的疑惑。

  loginStatus$ = this.authService.getLoginStatus().pipe(
    switchMap(res => {
      if (!res) { return this.authService.isLoggedIn(); }
      else { return of(res); }
    }),
    tap(res => console.log('tap: ' + res)),
    //shareReplay(1),
 );

isLoggedIn 是一个 http 请求,用于检查我是否登录,我检查 switchMap 中的 loginStatus 值,如果为假,则调用 isLoggedIn

isLoggedIn(): Observable<boolean> {
console.log('called isLoggedIn');
return this.httpService.get('/auth/isLoggined').pipe(
  map(res => {
    console.log('isLoggedIn res: ' + JSON.stringify(res));
    if (res) {
      this.loginStatus.next(true);
      this.firstName = res.first_name;
      this.lastName = res.last_name;
      this.tel = res.tel;
      return 'fool';
    } else {
      this.clearAll();
      return false;
    }
  }),

最后是我的控制台结果:

called isLoggedIn
called isLoggedIn
called isLoggedIn
called isLoggedIn

isLoggedIn 被调用了 4 次,因为我的 4 个异步管道,并且 loginStatus 为 false。

当第一个 http 请求响应时,我得到了一个 json 对象,如 {first_name:xxx, last_name:yyy, 1234},所以我将 true 推送到 loginStatus 和 return a 'fool' 给进一步的操作员。

我想我会在 loginStatus$ 的 tap 运算符中得到傻瓜,但事实并非如此。

isLogedIn res: {"logined":true,"first_name":"xxx","last_name":"yyy","tel":"1234"}
tap: true
tap: true
tap: true
tap: true

我只得到了第一个 isLoggedIn 请求的响应,其他 3 个都丢失了。 而且我从来没有得到傻瓜,我猜所有的异步管道都从 loginStatus BehaviorSubject 得到 true。

然后我在isLoggedIn函数中注释了this.loginStatus.next(true)来比较结果

isLogedIn res: {"logined":true,"first_name":"xxx","last_name":"yyy","tel":"1234"}
tap: fool
isLogedIn res: {"logined":true,"first_name":"xxx","last_name":"yyy","tel":"1234"}
tap: fool
isLogedIn res: {"logined":true,"first_name":"xxx","last_name":"yyy","tel":"1234"}
tap: fool
isLogedIn res: {"logined":true,"first_name":"xxx","last_name":"yyy","tel":"1234"}
tap: fool

这是我喜欢的结果,我打了 4 次电话,所以我得到了 4 次回应,它把傻瓜传给了水龙头 4 次。

我想知道为什么我在第一次订阅的过程中更改了BehaviorSubject时,另一个reponse丢失了。
以及为什么所有 4 个订阅都获得真实值,我想至少第一次会得到傻瓜。
这是否意味着 BehaviorSubject 将在更改时取消进一步的过程而不是完成它?

请试试这个

       private loginStatus = new BehaviorSubject<boolean>(false);
        
        getLoginStatus(): Observable<boolean> {
          return this.loginStatus.asObservable();
        }
    
        this.loginStatus$ = this.getLoginStatus().pipe(
          mergeMap((res) => {
            if (!res) {
              return this.isLoggedIn();
            } else {
              return of(res);
            }
           }),
           tap(res => console.log('tap: ' + res)),
         );
      }

这给出了预期的 4 个结果。

发生这种情况是因为 switchMap 运算符。

当你调用 this.loginStatus.next(true); 时,它会切换到最新事件,并从 this.loginStatus$ 可观察到并在 else 内部执行语句,并在 tap true 中打印时间。

如果您设置 this.loginStatus.next(false); 这将设置为无限循环。

结论

如果你输入任意数量的 loginStatus$ | async 因为你正在使用 switchMap,内部 observable 将被调用一次。

switchMap 之后的 tap 管道将根据 | async 管道订阅数调用。

发生这种情况是因为您正在使用 switchMap,这将取消之前的内部可观察对象并处理新的。

因此,当您使用 this.loginStatus.next(true)BehaviorSubject 中发出一个值时,switchMap 运算符将取消先前的可观察值并处理新值(为真)。

如果你想实现你的场景,你应该保持之前的内部观察正常完成,那么你需要使用mergeMapconcatMap运算符。