我第一次尝试响应式编程,使用 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):  ────────────────────>

我知道我们可以使用 throttledebounce 来处理这类情况,但它们仍然容易受到(更大的)计时问题的影响。例如,连接不稳定的用户(请求时间在 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 函数对带有 flatMapdiff 的普通 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/

10-09 23:33