我第一次尝试响应式编程,使用 bacon.js ,并遇到了一个让我烦恼的奇怪交互。我想出了这个事件链的一个工作版本,但是骇人听闻的额外流操作使它从代码的其余部分中脱颖而出——正如你将看到的。
我发现 FRP/Bacon.js 可以生成一些非常干净的代码,所以感觉就像我只是缺少一些明显的运算符。问题来了,好吗?
几个来源建议使用 Observable.flatMapLatest
来丢弃陈旧的事件。假设我们要为输入流中的每个事件完成一个 IO 操作。在下图中, i
代表输入流事件, rN
s 代表每个请求流的事件, x
es 标记请求流被取消的时间点。这些很可能是单事件流,因此 .
意味着该流在事件发生后实际上已关闭(如果之前没有取消!)。
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 研究也很有值(value)。如果现在还不清楚,这就是我想要做的:
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
进行操作。但这不仅看起来很笨拙,它还添加了时间戳/ID 作为请求的要求,以便我们稍后对其进行排序。那么我想我的问题是:我错过了什么?有一个更好的方法吗?如何对每个
takeUntil
事件流(请求)而不是外部(输入)进行 flatMap
?任何帮助表示赞赏,谢谢!
最佳答案
这是一个使用 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();
};
http://jsbin.com/doreqa/3/edit?js,console
关于javascript - Observable.flatMapLatest : Cancel at a deeper level?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29850928/