这适用于redux-observable
,但我认为一般模式对于rxjs
非常通用
我有一系列事件(来自redux-observable
,这些是redux操作),我特别想要为同一个"资源"配对两种不同类型的事件。 - "资源集活跃"和#34;资源加载" - 当这些事件与#34;匹配时,发出新事件。问题是这些可以以任何顺序进入任何资源,并且可以多次触发。您可以在加载之前设置一些活动的东西,或者在设置为活动之前加载某些东西,并且可以在其间加载或设置其他资源。
我想要的是一个"这个资源现已加载,现在已经处于活动状态" - 这也意味着一旦加载了资源,就可以认为它永远被加载了。
如果这些事件没有被资源ID键入,那么它将非常简单:
首先我会按类型拆分它们:
const setActive$ = action$.filter(a => a.type == 'set_active');
const load = action$.filter(a => a.type == 'loaded');
在没有键控的简单情况下,我会说:
const readyevent$ = setActive$.withLatestFrom(loaded$)
然后readyevent$
只是set_active
个事件的流,其中至少有一个loaded
事件。
但我的问题是set_active
和loaded
事件都是由资源ID键入的,并且由于我无法控制的原因,在两个事件中标识资源的属性是不同的。所以这就像:
const setActive$ = action$.filter(a => a.type === 'set_active').groupBy(a => a.activeResourceId);
const loaded$ = action$.filter(a => a.type === 'loaded').groupBy(a => a.id);
但从这一点来说,我无法确定如何重新加入
"这两个分组可观察流在同一个密钥上,这样我就可以发出withLatestFrom
个动作流。
答案 0 :(得分:1)
我相信这就是你所描述的:
const action$ = Rx.Observable.from([
{ activeResourceId: 1, type: 'set_active' },
{ id: 2, type: 'loaded' },
{ id: 1, type: 'random' },
{ id: 1, type: 'loaded' },
{ activeResourceId: 2, type: 'set_active' }
]).zip(
Rx.Observable.interval(500),
(x) => x
).do((x) => { console.log('action', x); }).share();
const setActiveAction$ = action$.filter(a => a.type === 'set_active')
.map(a => a.activeResourceId)
.distinct();
const allActive$ = setActiveAction$.scan((acc, curr) => [...acc, curr], []);
const loadedAction$ = action$.filter(a => a.type === 'loaded')
.map(a => a.id)
.distinct();
const allLoaded$ = loadedAction$.scan((acc, curr) => [...acc, curr], []);
Rx.Observable.merge(
setActiveAction$
.withLatestFrom(allLoaded$)
.filter(([activeId, loaded]) => loaded.includes(activeId)),
loadedAction$
.withLatestFrom(allActive$)
.filter(([loadedId, active]) => active.includes(loadedId))
).map(([id]) => id)
.distinct()
.subscribe((id) => { console.log(`${id} is loaded and active`); });

<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.10/Rx.min.js"></script>
&#13;
基本方法是为每个操作类型创建一个不同的流,并将其与另一个的不同聚合连接起来。然后合并两个流。当匹配的setActive和加载事件时,这将发出值。合并结束时的distinct()
会使您只收到一次通知。如果您希望在初始操作之后对每个setActive操作发出通知,则只需删除该操作符。
答案 1 :(得分:0)
groupBy
执行此操作看起来有点复杂,那里有一个关键值,但你得到一个Observable of Observables - 所以可能有点难以做对。
我会将id映射到公共属性,然后使用扫描进行组合。我在我的应用程序中使用此模式进行分组。
扫描中的累加器是一个对象,用作关联数组 - 每个属性都是一个id,属性值是到目前为止累积的动作数组。
在扫描之后,我们转换为一个可观察的匹配操作数组流 - 有点像withLatestFrom
但有些数组可能尚未完成。
下一步是筛选我们认为完整的阵列 既然你说
至少有一个已加载的事件
我将假设存在两个或多个动作,其中至少有一个动作已加载&#39; - 但从你的问题中判断这是否足够,这有点棘手。
最后,重置累加器中的id,因为它可能会在稍后的流中再次出现。
const setActive$ = action$.filter(a => a.type === 'set_active')
.map(a => { return { id: a.activeResourceId, action: a } });
const loaded$ = action$.filter(a => a.type === 'loaded')
.map(a => { return { id: a.id, action: a } });
const accumulator = {};
const readyevent$: Observable<action[]> =
Observable.merge(setActive$, loaded$)
.scan((acc, curr) => {
acc[curr.id] = acc[curr.id] || [];
acc[curr.id].push(curr.action)
}, accumulator)
.mergeMap((grouped: {}) => Observable.from(
Object.keys(grouped).map(key => grouped[key])
))
.filter((actions: action[]) => {
return actions.length > 1 && actions.some(a => a.type === 'loaded')
})
.do(actions => {
const id = actions.find(a => a.type === 'loaded').id;
accumulator[id] = [];
});