Angular - 竞争条件 - 一次多个订阅

Posted

技术标签:

【中文标题】Angular - 竞争条件 - 一次多个订阅【英文标题】:Angular - race condition - multiple subscriptions at once 【发布时间】: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 方法来扮演用户的角色。但我有一种感觉,我这样做完全错了.. 【参考方案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

【讨论】:

以上是关于Angular - 竞争条件 - 一次多个订阅的主要内容,如果未能解决你的问题,请参考以下文章

在 Angular 7 中,为啥即使只订阅一次 HttpClient.post 也会执行两次?

具有多个订阅者的 Angular 2 Observable

如何在 Angular 和 RxJs 中管理多个顺序订阅方法?

NgrxStore 和 Angular - 大量使用异步管道或在构造函数中只订阅一次

Apollo - Angular 应用程序中子组件的多个 graphql 订阅

订阅多个字段的 valueChanges 导致反应形式 angular 2 的性能问题