使用 rxjs concat 多次调用 AuthorizeService isAuthenticated() 订阅

AuthorizeService isAuthenticated() subscribe being called multiple times with rxjs concat

我正在使用来自 visual studio 模板的默认客户端身份验证服务。

有一个打字稿 AuthorizeService,它有一个名为 isAuthenticated 的函数,它调用下面的函数并检查它是否为 null。

getUser 函数:

public getUser(): Observable<IUser> {
    return concat(
      this.userSubject.pipe(take(1), filter(u => !!u)),
      this.getUserFromStorage().pipe(filter(u => !!u), tap(u => this.userSubject.next(u))),
      this.userSubject.asObservable());
  }

在上述函数上调用 .subscribe 时。订阅被调用了三次。大概是针对 concat 函数中的每个可观察对象。使用上面的 getUser,我希望订阅被调用一次。我将如何实现?

我尝试将上面的内容转换为 returns 一个值的嵌套承诺,但由于某种原因没有成功,返回结果后,它 returns resolve(null) 即使用户存在于会话存储中

考虑改用 forkJoin(当所有 observable 完成时,将每个 observable 的最后一个发射值作为数组发出),如下所示:

注意:我已将 take 运算符添加到最后一个可观察对象,以便它完成

public getUser(): Observable <[IUser, IUser, IUser]> {
  return forkJoin(
    this.userSubject.pipe(take(1), filter(u => !!u)),
    this.getUserFromStorage().pipe(filter(u => !!u), tap(u => this.userSubject.next(u))),
    this.userSubject.asObservable().pipe(take(1))
  );
}

我假设您只希望 getUser() return 一个用户。现在,您的逻辑是“获取 userSubject 的当前值并从存储中获取用户,还获取 userSubject 的当前值和所有未来值。”

这意味着如果 userSubject 的值为 truthy,您将至少返回 3 个用户。这就是你的逻辑。

我不确定你所说的 'only subscribe once,' 是什么意思,但我假设你的意思是 'only return 1 user.' 一种简单的方法是只从你的 concat 调用中获取一个值:

return concat(...).pipe(take(1));

这可能会导致不可预知的行为。三个流中的任何一个首先发出一个值将是您获取的值。如果 getUserFromStorage() 需要一些时间才能完成,您将始终返回 null。我猜这就是你在嵌套承诺时发生的事情(尽管我必须查看你的代码才能确定)。

更好的方法是使用 switchMap 或 mergeMap(在这种情况下都可以)我还猜测如果 userSubject 中没有用户,您只想从后端获取用户。这种方法将有效地缓存当前经过身份验证的用户。

public getUser(): Observable<IUser> {
  return this.userSubject.pipe(
    take(1),
    mergeMap(u => {
      if(u) return of(u);
      return this.getUserFromStorage().pipe(
        tap(u => u && this.userSubject.next(u))
      );
    }), 
    take(1)
  );
}

这是做什么的?仅当来自 userSubject 的用户不真实时,它才会尝试从存储中获取用户。如果 userSubject 中有用户,则永远不会调用(或订阅)this.getUserFromStorage()。值得注意的是,如果 getUserFromStorage() 仅 return 一个值,则不需要第二次调用 take(1) 。这还假设如果存储中没有用户,getUserFromStorage() returns null。

最后,我删除了所有过滤器,因为(根据您的描述)您希望此流 return 如果主题中没有用户且存储中没有用户则为 null。如果我们过滤掉 null return 那么我们将永远不会 return null。相反,我所做的是,如果 getUserFromStorage() returns null.

我们只 return null