代码之家  ›  专栏  ›  技术社区  ›  Ben Thomson

使用ngrx嵌套switchMaps的潜在竞争条件

  •  0
  • Ben Thomson  · 技术社区  · 5 年前

    我有一个Angular 9应用程序,它在很大程度上使用了可观测值。在一个组件中,我需要:

    1. 获取所有公司(因为它们包含我需要的某些信息)
    2. 然后获取所有回复,并映射公司中的一些额外信息(即回复的公司名称)。
    3. 将该列表作为可观察性对象返回,该对象使用异步管道在模板中订阅。

    为此,我最初有以下代码(似乎有效):

    getPopulatedResponses(): Observable<ResponseModel[]> {
        return this.companyFilesStore
          .select(companyFilesSelector)
          .pipe(
            switchMap(companies => {
              return this.getAccessibleResponses(companies);
            })
          )
      }
    
    getAccessibleResponses(accessibleCompanies: CompanyFilesModel[]): Observable<ResponseModel[]> {
        return this.responsesStore
          .select(responsesSelector)
          .pipe(
            map((responses) => {
              return responses?.map((response) => {
                const company = accessibleCompanies?.find(c => c.companyGuid === response.companyId);
                response.companyName = company?.companyName;
                return response;
              }).sort((a, b) => {
                return a.completedDateTime < b.completedDateTime ? 1 : -1;
              })
            })
          )
    

    首先,我不确定switchMap是否是正确的操作符,因为如果companyFilesSelector正在更新,然后responsesSelector部分启动,这不会终止之前的订阅吗?

    现在,我还需要添加一个订阅到我拥有的过滤器服务,因此我对getAccessibleResponses方法进行了以下更改(该方法似乎仍然有效,但我觉得它可能更多地是通过运气/良好的连接而不是其他任何方式):

    getAccessibleResponses(
        accessibleCompanies: CompanyFilesModel[],
      ): Observable<ResponseModel[]> {
        return this.searchAndFilterService.getFilter()
          .pipe(
            switchMap(filter => {
              return this.responsesStore
                .select(responsesSelector)
                .pipe(
                  map((responses) => {
                    return responses?.map((response) => {
                      const company = accessibleCompanies?.find(c => c.companyGuid === response.companyId);
                      response.companyName = company?.companyName;
                      return response;
                    })
                      ?.filter(r => !r.isArchived)
                      ?.filter(r => !filter?.selectedCompany || r.companyId === filter.selectedCompany.companyId)
                      ?.filter(r => !filter.selectedAssessmentTemplate ||
                        r.assessmentTemplateId === filter.selectedAssessmentTemplate.assessmentTemplateId)
                      .sort((a, b) => {
                        return a.completedDateTime < b.completedDateTime ? 1 : -1;
                      })
                  })
                )
            })
          )
      }
    

    我相当有信心这不是最好的甚至是正确的方法,但我有点迷失了方向,很难理解如何实现以下目标:

    1. 把所有公司放在首位。
    2. 将可观察到的公司结果与回复相结合。
    3. 当过滤器更改时(即过滤器服务可观察),过滤1/2的结果。

    任何帮助或指导都将不胜感激。

    0 回复  |  直到 5 年前
        1
  •  1
  •   Andrei Gătej    5 年前

    据我所知,你有三个主要部分需要处理( companies , responses filter )每当其中一个部分发生变化(例如发出新值)时,您都希望根据变化的内容重新计算数据。

    如果是这样的话,假设你的应用程序状态被保存在 单一真相来源 ,我会这样做:

    const companies$ = this.store.select(companyFilesSelector);
    
    const accessibleResponses$ = this.store.select(responsesSelector);
    
    const activeFilter$ = this.store.select(activeFilterSelector);
    
    const data$ = combineLatest(
      companies$,
      accessibleResponses$,
      activeFilter$,
    ).pipe(
      map(([companies, responses, filter]) => {
        // Logic here...
      })
    )
    

    combineLatest 这里使用的是因为您想在3个部分之一发生变化时计算显示的数据。

    所以,如果你的初始状态可能是:

    {
     companies: [],
     responses: [],
     activeFitler: null,
    }
    

    例如,每当您添加一些新公司时,提供的功能 map 将被调用,以便根据新公司更新向用户显示的内容。添加新内容时也会发生同样的情况 响应 或者一个新的 滤波器 .


    编辑

    我可以使用其中的switchMap,这样订阅就不会查看该存储以节省内存吗?

    我不认为这能节省多少内存,因为 switchMap 它是否取消订阅活动可观察对象,并根据新到达的对象创建一个新的可观察对象 价值 以及 提供的功能 .

    此外,由于您订阅了该存储,因此应该进行第一次数据检索 同步 因为我想这家店是一种 BehaviorSubject (例如NgRx)