Angular - 竞争条件 - 一次多个订阅
Angular - race condition - multiple subscriptions at once
我有一个 UserService
,它有一个 getUser
方法,return 是一个 User
class.
类型的可观察对象
getUser
检查用户是否已经存在,如果存在,则 return 是该用户,否则它会创建一个新用户。
创建新用户时,也会对用户的'roles'数据进行API调用,代码如下:
public getUser(): Observable<User> {
if (!UserService.user) {
UserService.user = new User();
return this.getGroups().pipe(
map(response => {
response.value.map(v => this.setRol(v));
return UserService.user;
}));
}
else {
return of(UserService.user);
}
}
但是有个问题
当我从两个不同的组件订阅此方法时,一个将遵循第一个 if 语句中的路径,第二个将 return else 语句中创建的用户。
但是第二个 return 是在第一个订阅完成 API 调用以获取用户所属的角色(组)之前创建的用户。这给了我错误。
我该如何解决这个问题?解决此问题的最佳做法是什么?
我会在 map
中设置 UserService.user = new User();
来解决这个问题。
public getUser(): Observable<User> {
if (!UserService.user) {
// remove from here
// UserService.user = new User();
return this.getGroups().pipe(
map(response => {
// add here
UserService.user = new User();
response.value.map(v => this.setRol(v));
return UserService.user;
}));
}
else {
return of(UserService.user);
}
}
这样 UsesrService.user
只会在 API 调用完成时设置。
这里有很多选择,
最简单的方法是在 map
运算符中创建您的用户并改善条件。
public getUser(): Observable<User> {
return !!UserService.user?.name // name or another property that we will help us understand that the user has initialized
? of(UserService.user)
: this.getGroups().pipe(
map(response => {
UserService.user = new User();
response.value.map(v => this.setRol(v));
return UserService.user;
}));
}
PS:通常建议当你有if-else
条件时首先从true
部分开始。
当您调用 getUser
方法时,它首先检查 UserService.user
的值,如果不存在则调用 API。但是,当您在两个 components
中注入相同的服务时,它会调用此方法两次(我们知道)。因此,当第二次调用它时,您将 UserService.user
分配给用户实例,并且您的 else
将始终被调用。 observer
模式很好用,确实很好用,angular也推荐。
public getUser(): Observable<User> {
return new Observable(observer => {
if (!UserService.user.checkRolesExist()) {
UserService.user = new User();
return this.getGroups().pipe(
map(response => {
response.value.map(v => this.setRol(v));
observer.next(UserService.user);
observer.complete();
}));
} else {
observer.next(UserService.user);
observer.complete();
}
});
}
现在,要检查用户是否存在,请在 User
class 上创建一个方法,如果角色存在或不存在,它可以得到你,它只是一个指导你的示例:
public checkRolesExist() {
return UserService.user?.length > 0
}
缓存shareReplay(1)
RxJS 使一种简单的缓存形式(像这样)变得非常容易
private userCache$ = this.getGroups().pipe(
map(({value}) => {
UserService.user = new User();
value.map(v => this.setRol(v));
return UserService.user;
}),
shareReplay(1)
);
public getUser(): Observable<User> {
return userCache$;
}
现在 this.getGroups()
只会在第一次订阅 userCache$
时被调用。之后,每个其他调用都等待 API 调用完成 and/or 获取该调用的缓存结果。
我有一个 UserService
,它有一个 getUser
方法,return 是一个 User
class.
getUser
检查用户是否已经存在,如果存在,则 return 是该用户,否则它会创建一个新用户。
创建新用户时,也会对用户的'roles'数据进行API调用,代码如下:
public getUser(): Observable<User> {
if (!UserService.user) {
UserService.user = new User();
return this.getGroups().pipe(
map(response => {
response.value.map(v => this.setRol(v));
return UserService.user;
}));
}
else {
return of(UserService.user);
}
}
但是有个问题
当我从两个不同的组件订阅此方法时,一个将遵循第一个 if 语句中的路径,第二个将 return else 语句中创建的用户。
但是第二个 return 是在第一个订阅完成 API 调用以获取用户所属的角色(组)之前创建的用户。这给了我错误。
我该如何解决这个问题?解决此问题的最佳做法是什么?
我会在 map
中设置 UserService.user = new User();
来解决这个问题。
public getUser(): Observable<User> {
if (!UserService.user) {
// remove from here
// UserService.user = new User();
return this.getGroups().pipe(
map(response => {
// add here
UserService.user = new User();
response.value.map(v => this.setRol(v));
return UserService.user;
}));
}
else {
return of(UserService.user);
}
}
这样 UsesrService.user
只会在 API 调用完成时设置。
这里有很多选择,
最简单的方法是在 map
运算符中创建您的用户并改善条件。
public getUser(): Observable<User> {
return !!UserService.user?.name // name or another property that we will help us understand that the user has initialized
? of(UserService.user)
: this.getGroups().pipe(
map(response => {
UserService.user = new User();
response.value.map(v => this.setRol(v));
return UserService.user;
}));
}
PS:通常建议当你有if-else
条件时首先从true
部分开始。
当您调用 getUser
方法时,它首先检查 UserService.user
的值,如果不存在则调用 API。但是,当您在两个 components
中注入相同的服务时,它会调用此方法两次(我们知道)。因此,当第二次调用它时,您将 UserService.user
分配给用户实例,并且您的 else
将始终被调用。 observer
模式很好用,确实很好用,angular也推荐。
public getUser(): Observable<User> {
return new Observable(observer => {
if (!UserService.user.checkRolesExist()) {
UserService.user = new User();
return this.getGroups().pipe(
map(response => {
response.value.map(v => this.setRol(v));
observer.next(UserService.user);
observer.complete();
}));
} else {
observer.next(UserService.user);
observer.complete();
}
});
}
现在,要检查用户是否存在,请在 User
class 上创建一个方法,如果角色存在或不存在,它可以得到你,它只是一个指导你的示例:
public checkRolesExist() {
return UserService.user?.length > 0
}
缓存shareReplay(1)
RxJS 使一种简单的缓存形式(像这样)变得非常容易
private userCache$ = this.getGroups().pipe(
map(({value}) => {
UserService.user = new User();
value.map(v => this.setRol(v));
return UserService.user;
}),
shareReplay(1)
);
public getUser(): Observable<User> {
return userCache$;
}
现在 this.getGroups()
只会在第一次订阅 userCache$
时被调用。之后,每个其他调用都等待 API 调用完成 and/or 获取该调用的缓存结果。