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 获取该调用的缓存结果。