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

Potential race condition using ngrx nested switchMaps

我有一个 Angular 9 应用程序,它大部分使用可观察对象。在一个组件中,我需要:

  1. 获取所有公司(因为它们包含我需要的某些信息)
  2. 然后获取所有回复并映射公司中的一些额外信息(即回复的公司名称)。
  3. Return 列为使用异步管道在模板中订阅的 observbe。

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

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 方法进行了以下更改(这似乎再次起作用,但我觉得它可能更多地通过 luck/good 连接比什么都重要):

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. 过滤器改变时(即过滤器服务observable)过滤1/2的结果。

如有任何帮助或指导,我们将不胜感激。

据我所知,您有 3 个主要部分可以使用(companiesresponsesfilter),只要其中一个部分发生变化(例如发出新值),您想根据更改的内容重新计算数据。

如果是这种情况并假设您的应用程序状态保存在单一真实来源中,我会这样做:

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 的功能,以便根据新公司更新向用户显示的内容。当您添加新的 responses 或新的 filter.

时,也会发生同样的情况

编辑

could I use a switchMap from that so no subscription looks at that store to potentially save memory?

我认为它不会节省太多内存,因为 switchMap 所做的一切都是为了取消订阅活动的可观察对象并根据新到达的 值创建一个新的可观察对象 提供的函数

此外,由于您订阅了商店,第一次数据检索应该同步,因为我认为商店是一种BehaviorSubject(例如 NgRx )