使用 ngrx 嵌套 switchMaps 的潜在竞争条件
Potential race condition using ngrx nested switchMaps
我有一个 Angular 9 应用程序,它大部分使用可观察对象。在一个组件中,我需要:
- 获取所有公司(因为它们包含我需要的某些信息)
- 然后获取所有回复并映射公司中的一些额外信息(即回复的公司名称)。
- 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;
})
})
)
})
)
}
我相当有信心这不是最好的方法,甚至不是正确的方法,但我有点迷茫并且很难理解如何实现以下目标:
- 首先获取所有公司。
- 将可观察到的公司的结果与回复相结合。
- 过滤器改变时(即过滤器服务observable)过滤1/2的结果。
如有任何帮助或指导,我们将不胜感激。
据我所知,您有 3 个主要部分可以使用(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
的功能,以便根据新公司更新向用户显示的内容。当您添加新的 responses
或新的 filter
.
时,也会发生同样的情况
编辑
could I use a switchMap from that so no subscription looks at that store to potentially save memory?
我认为它不会节省太多内存,因为 switchMap
所做的一切都是为了取消订阅活动的可观察对象并根据新到达的 值创建一个新的可观察对象 和 提供的函数 。
此外,由于您订阅了商店,第一次数据检索应该同步,因为我认为商店是一种BehaviorSubject
(例如 NgRx )
我有一个 Angular 9 应用程序,它大部分使用可观察对象。在一个组件中,我需要:
- 获取所有公司(因为它们包含我需要的某些信息)
- 然后获取所有回复并映射公司中的一些额外信息(即回复的公司名称)。
- 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;
})
})
)
})
)
}
我相当有信心这不是最好的方法,甚至不是正确的方法,但我有点迷茫并且很难理解如何实现以下目标:
- 首先获取所有公司。
- 将可观察到的公司的结果与回复相结合。
- 过滤器改变时(即过滤器服务observable)过滤1/2的结果。
如有任何帮助或指导,我们将不胜感激。
据我所知,您有 3 个主要部分可以使用(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
的功能,以便根据新公司更新向用户显示的内容。当您添加新的 responses
或新的 filter
.
编辑
could I use a switchMap from that so no subscription looks at that store to potentially save memory?
我认为它不会节省太多内存,因为 switchMap
所做的一切都是为了取消订阅活动的可观察对象并根据新到达的 值创建一个新的可观察对象 和 提供的函数 。
此外,由于您订阅了商店,第一次数据检索应该同步,因为我认为商店是一种BehaviorSubject
(例如 NgRx )