【问题标题】:Angular - race condition - multiple subscriptions at onceAngular - 竞争条件 - 一次多个订阅
【发布时间】:2021-08-16 16:31:32
【问题描述】:

我有一个UserService,它有一个getUser 方法,该方法返回一个User 类的可观察对象。

getUser 检查用户是否已经存在,如果存在,则返回该用户,否则创建一个新用户。

当创建一个新用户时,它也会对用户的“角色”数据进行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 语句中的路径,第二个将返回 else 语句中创建的用户。

但第二个在第一个订阅完成 API 调用以获取用户所属的角色(组)之前返回创建的用户。这给了我错误。

我该如何解决这个问题?解决此问题的最佳做法是什么?

【问题讨论】:

  • UserService.user 可能是可观察的,您可以使用 last
  • 这两个组件你想要什么?但这不是竞争条件,它可以正常工作,为什么我可以向您解释。
  • @ApoorvaChikara 在这两个组件中,我都需要用户并检查用户的角色。通过调用执行 http 调用的 getGroups 方法来扮演用户的角色。但我有一种感觉,我这样做完全错了..

标签: angular rxjs race-condition subscription


【解决方案1】:

缓存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 调用完成和/或获取该调用的缓存结果。

【讨论】:

    【解决方案2】:

    我会在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调用完成时设置。

    【讨论】:

      【解决方案3】:

      这里有很多选择,

      最简单的方法是在 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 部分开始。

      【讨论】:

        【解决方案4】:

        当你调用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 类上创建一个方法,该方法可以获取角色是否退出,这只是一个指导您的示例:

        public checkRolesExist() {
           return UserService.user?.length > 0
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-05-20
          • 1970-01-01
          • 2021-10-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多