【问题标题】:Potential race condition using ngrx nested switchMaps使用 ngrx 嵌套 switchMaps 的潜在竞争条件
【发布时间】:2020-04-16 00:07:34
【问题描述】:

我有一个 Angular 9 应用程序,大部分情况下使用 observables。在一个组件中,我需要:

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

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

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

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

【问题讨论】:

  • 好像没有异步动作吧?
  • 所以理论上你可以让所有公司和所有响应异步,然后映射两个可观察对象的结果以返回完整的对象,但目前它不能异步工作,我不是 100% 的最佳方式这样做。

标签: angular typescript rxjs ngrx switchmap


【解决方案1】:

据我所知,您需要使用 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 时,也会发生同样的情况。


编辑

我是否可以使用其中的 switchMap,以便没有订阅者查看该存储以节省内存?

我认为它不会节省太多内存,因为 switchMap 所做的一切都是基于新到达的 value提供的功能

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

【讨论】:

  • 这就是我要找的。由于公司很少更改,我可以使用 switchMap ,因此没有订阅者会查看该商店以节省内存?
  • 实际上我需要的可能有点不同,因为必须先加载公司,然后才能处理响应!
  • @BenThomson 我已经更新了我的答案。 '必须先加载公司,然后才能处理响应' 嗯,难道所有东西都不能适应map 的回调吗?因为我不会说您正在加载任何内容,但您正在选择商店的部分内容。所以我想你可以检查那个cb是否companies已经到了。
  • 是的,在阅读了您的 cmets 并仔细考虑之后,也许可以在某处对公司进行过滤检查!= null 以确保它们已被加载。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-24
  • 2021-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多