我第一次使用bacon.js尝试使用反应式编程,然后点击一个令我讨厌的奇怪互动。我想出了这个事件链的工作版本,但是hackish额外的流操作让它从代码的其余部分中脱颖而出 - 正如你所看到的那样。
我发现FRP / Bacon.js生成了一些非常干净的代码,所以我觉得我只是缺少一些明显的操作符。对于这个问题,我们应该吗?
有几个消息来源建议使用Observable.flatMapLatest
来丢弃陈旧事件。假设我们想要为输入流中的每个事件完成IO操作。在下图中,i
表示输入流事件,rN
表示每个请求流的事件,x
表示请求流被取消的时间点。这些很可能是单事件流,因此.
表示流在事件发生后实际关闭(如果之前没有取消!)。
input stream: i──i────────i───i─────────>
request: │ │ │ └────r4.──>
request: │ │ └───x─r3.─────>
request: │ └──r2.───x─────────────>
request: └──x─────r1.──────────────>
+
i.flatMapLatest(requests): ──────r2─────────────r4───>
对于几个用例来说这是完全可以的,但要注意r3
:它永远不会被接受,即使它在r4
到达之前仍然是有效的响应。在最糟糕的情况下,浪费了大量请求:
input stream: i──i──i───i──i──────>
request: │ │ │ └──x─r4.──>
request: │ │ └───x─r3.─────>
request: │ └──x─r2.─────────>
request: └──x─────r1.────────>
+
i.flatMapLatest(requests): ────────────────────>
我知道我们可以使用throttle
和debounce
来处理这类情况,但他们仍然容易受到(更大的)时间问题的影响。例如,一个参差不齐的连接上的用户(请求需要0.5s-5s +)可能会受此严重影响。
现在,即使它不是一个真正的问题,它看起来对我的FRP研究也很有价值。如果现在还不清楚,这就是我想要做的事情:
input stream: i──i────────i───i─────────>
request: │ │ │ └────r4.──>
request: │ │ └─────r3.─x───>
request: │ └──r2.──────────x──────>
request: └──────x─r1.──────────────>
+
i.flatMap_What?(requests): ──────r2──────────r3─r4───>
这是我当前的实施与基本的flatMapLatest
:
var wrong = function (interval, max_duration) {
var last_id = 0,
interval = interval || 1000,
max_duration = max_duration || 5000;
Bacon.interval(interval, 'bang').log('interval:')
.flatMapLatest(function () {
return Bacon.later(Math.random()*max_duration, last_id++).log(' request:');
})
.log('accepted: %s **********');
};
var right = function (interval, max_duration) {
var last_id = 0,
interval = interval || 1000,
max_duration = max_duration || 5000;
Bacon.interval(interval, 'bang').log('interval:')
.flatMap(function () {
return Bacon.later(Math.random()*max_duration, last_id++).log(' request:');
})
.diff(-1, function (a, b) { return b > a ? b : -1 })
.filter(function (id) { return id !== -1 })
.log('accepted: %s **********');
};
wrong
函数会按预期忽略连续请求,而right
函数则在flatMap
和diff
的普通filter
上运行。但是不仅看起来非常hacky,它还将timestamp / id添加为请求的要求,这样我们就可以稍后对其进行排序。
我想我的问题是,那么:我错过了什么?有一个更好的方法吗?我如何takeUntil
每个flatMap
事件流(请求)而不是外部(输入)?
感谢任何帮助,谢谢!
答案 0 :(得分:0)
这是一个使用max id和scan的解决方案:
var right = function (interval, max_duration) {
var bang_id = 0, last_id = 0,
interval = interval || 1000,
max_duration = max_duration || 5000;
Bacon.interval(interval, 'bang')
.map(function() { return 'Bang'+(bang_id++) })
.flatMap(function (val) {
return Bacon.later(Math.random()*max_duration, {value: val, id: last_id++});
})
.scan({id: -1, value: null}, function(memo, v) {
if(v.id <= memo.id) {
return memo;
}
return v;
})
.changes()
.skipDuplicates(function(a, b) { return a.id === b.id; })
.map('.value')
.log();
};