我正在使用Visual Studio模板中的默认客户端身份验证服务。
有一个打字稿AuthorizeService,它具有一个名为isAuthenticated的函数,该函数将调用以下函数并检查其是否为空。
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,我希望订阅被调用一次。我将如何实现?
我尝试将上述内容转换为返回一个值但由于某种原因而没有成功的嵌套承诺,在返回结果后,即使用户存在于会话存储中,它也会返回resolve(null)
答案 0 :(得分:0)
考虑使用forkJoin
代替(所有可观察对象完成后,将每个对象的最后一个发出的值作为数组发出),如下所示:
注意:我在最后一个可观察到的位置添加了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))
);
}
答案 1 :(得分:0)
我假设您希望getUser()仅返回一个用户。现在,您的逻辑说:“获取userSubject的当前值,并从存储中获取用户,并获取userSubject的当前值和所有将来值。”
这意味着,如果userSubject的值是true,则将至少获得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。