我在Angular应用程序中有相同组件的多个实例。组件的每个实例将调度一个动作[Action] LoadData
,以从外部API加载数据。动作的有效负载具有有关分派动作的组件的信息,以便能够将其存储在store
中正确的对应位置。
无论操作中包括其他任何有效负载,每次获取的数据都是相同的。既然是这种情况,就不需要我进行多个API调用来加载相同的数据,因此我可以使用switchMap
取消以前所有未完成的调用。
为此,我有两种效果,一种是加载数据:
@Effect({ dispatch: false })
loadData$ = this.actions$.pipe(
ofType<LoadData>(LOAD_DATA),
switchMap((action) => { // This will cancel out any previous service call
return this.service.loadData().pipe(
tap(val => {
console.log(`Loading data...`)
this.dataSubject$.next(val) // When the data is fetched, emit on the subject
})
);
})
);
和一个处理已调度动作并与数据结合的动作
@Effect()
handleActionAndDataLoaded$ = combineLatest(
this.actions$.pipe(
ofType<LoadData>(LOAD_DATA)
),
this.dataSubject$.asObservable()
).pipe(
tap(_ => console.log(`All done`))
)
,其中dataSubject$
是Subject
。
这是给我带来棘手的部分的地方。如果已n
次调度了LoadData
次操作,然后需要处理数据,我需要n
次触发第二个效果并与其他行动有效载荷相结合。我现在得到的是第二种效果的一个触发。
阅读combineLatest
并查看大理石图,这当然是预期的行为。
用大理石图表示,我得到类似的东西
我需要的是下面的内容。
我知道还有其他方法可以通过更改应用程序体系结构中的其他内容来解决整个问题,但这是我目前必须使用的方法,在我看来,这很有趣{{1} }问题!
如何实现?我想念什么运算符组合?
答案 0 :(得分:1)
我假设它的外观应与第一个图更相似:
actions -1-2-3-----------
data ---------B-------
result ---------(1B2B3B)
否则,您似乎希望LoadData操作与以前提取的数据配对。
您可以使用合并和扫描来创建状态机:
const createEffectWithScan = (actions$: Observable<string>, subject$: Observable<string>) =>
merge(
actions$.pipe(map(x => ({ type: 'action', value: x }))),
subject$.pipe(map(x => ({ type: 'data', value: x }))),
).pipe(
scan(
(a, b) => b.type === 'action'
? { data: null, toEmit: [], buffer: [...a.buffer, b.value] }
: { data: b.value, toEmit: a.buffer, buffer: [] }
, { data: null, buffer: [], toEmit: [] }
),
filter(x => x.data !== null),
concatMap(x => x.toEmit.map(a => a + x.data))
);
正如您所提到的,这是一个不错的练习,但应该以其他方式解决。强制性地通过主题戳值不是一个好的解决方案。