我至少有两个按钮,我想动态收听点击次数。 listeningArray$
会发出一个我需要收听的按钮数组(ar
)。当有人点击我正在收听的其中一个按钮时,我需要控制点击按钮的日志,并记录时间间隔内的值。
如果ar
从[1,2]
转到[1]
,我们需要停止收听按钮#2上的点击次数。因此,需要删除2
的DOM点击事件,这应触发.finally()
运算符。但是对于1
,我们应该继续订阅并且.finally()
内的代码不应该运行,因为没有任何内容被取消订阅。
const obj$ = {};
Rx.Observable.combineLatest(
Rx.Observable.interval(2000),
listeningArray$ // Will randomly emit either [1] or [1,2]
)
.switchMap(([x, ar]) => {
const observables = [];
ar.forEach(n => {
let nEl = document.getElementById('el'+n);
obj$[n] = obj$[n] || Rx.Observable.fromEvent(nEl, 'click')
.map(()=>{
console.log(' el' + n);
})
.finally(() => {
console.log(' FINALLY_' + n);
});
observables.push(obj$[n]);
})
return Rx.Observable.combineLatest(...observables);
})
.subscribe()
但是,每当间隔发出一个值时,发生的事情是,DOM事件ALL被删除,然后立即再次添加,并且.finally运算符内的代码运行1
和 2
。
这真让我很沮丧。我错过了什么?
这有点复杂,所以我创建了这个:https://jsfiddle.net/mfp22/xtca98vx/7/
答案 0 :(得分:1)
FRP中的国家是不可改变的。因此,当您将map切换到第二个发射时,包含combineLatest
的先前可观察[1,2]
将被取消订阅并调用finally运算符。在订阅下一个仅包含[1]
如果您只想取消订阅一个按钮,则可以在DOM中存储状态(将atr添加到按钮)并使用filter
忽略按钮。
或者你可以在每个按钮上添加一个TakeWhile()
,指示何时应该取消订阅,以便它可以调用它自己的finally()
答案 1 :(得分:1)
我其实非常接近,但我误解了switchMap
的观点。
switchMap
旨在取消订阅每次从上方发出新值时返回的observable。这就是为什么它可以用来取代旧的挂起Http请求,而不是需要新的请求。
我遇到的问题是可以预料到的。在订阅当前的观察之前,switchMap
将取消订阅之前返回的观察者。正如我在问题中解释的那样,这是不可接受的。这是不可接受的原因是,在我的实际项目中,fromEvent
观察者正在收听Firebase child_added
事件,因此当这些冷观察者从没有订阅者变为拥有1个订阅者时,Firebase将随后解雇已经存在的每个孩子的事件,以及未来的孩子的事件。
我和mergeMap
玩了一段时间,但是手动取消订阅以前返回的观察值非常困难和错误。
所以我为内部observable添加了一个订阅者,而switchMap
正在执行unsubscribe from old => subscribe to new
的进程,以便始终有一个订阅者。我使用takeUntil(Observable.timer(0))
来确保订阅者没有建立并导致内存泄漏。
可能有更好的解决方案,但这是我找到的最好的解决方案。
const obj$ = {};
Rx.Observable.combineLatest(
Rx.Observable.interval(2000),
listeningArray$ // Will randomly emit either [1] or [1,2]
)
.switchMap(([x, ar]) => {
const observables = [];
ar.forEach(n => {
let nEl = document.getElementById('el'+n);
obj$[n] = obj$[n] || Rx.Observable.fromEvent(nEl, 'click')
.map(()=>{
console.log(' el' + n);
})
.finally(() => {
console.log(' FINALLY_' + n);
})
.share();
obj$[n].takeUntil(Rx.Observable.timer(0))
.subscribe();
observables.push(obj$[n]);
})
return Rx.Observable.combineLatest(...observables);
})
.subscribe()
我还必须添加.share()
方法。无论如何我还是需要它。我正在使用这种模式让一些Angular组件声明他们需要什么数据,忽略其他组件可能需要的东西,以实现更好的关注点分离。因此,多个组件可以订阅相同的Firebase可观察对象,但.share()
运算符可确保来自Firebase的每条消息仅处理一次(我将每个消息的调度操作分配给Redux存储)。