2

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

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

获取用户函数:

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 时。订阅被调用了 3 次。大概对于 concat 函数中的每个 observable 。使用上面的 getUser,我希望 subscribe 被调用一次。我将如何实现这一目标?

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

4

2 回答 2

1

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

这意味着如果 userSubject 的值为真,您将至少获得 3 个用户。这就是你的逻辑所说的。

我不确定您所说的“仅订阅一次”是什么意思,但我假设您的意思是“仅返回 1 个用户”。一种简单的方法是只从你的 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() 只返回一个值,则不需要第二次调用 take(1)。这还假设 getUserFromStorage() 如果存储中没有用户,则返回 null。

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

于 2020-08-31T16:29:26.853 回答
0

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

注意:我已将take运算符添加到最后一个 observable 以便它完成

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))
  );
}
于 2020-08-31T14:35:49.893 回答