Observable.flatMapLatest:在更深层次取消?

时间:2015-04-24 15:02:39

标签: javascript reactive-programming 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):  ────────────────────>   

我知道我们可以使用throttledebounce来处理这类情况,但他们仍然容易受到(更大的)时间问题的影响。例如,一个参差不齐的连接上的用户(请求需要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函数则在flatMapdiff的普通filter上运行。但是不仅看起来非常h​​acky,它还将timestamp / id添加为请求的要求,这样我们就可以稍后对其进行排序。

我想我的问题是,那么:我错过了什么?有一个更好的方法吗?我如何takeUntil每个flatMap事件流(请求)而不是外部(输入)?

感谢任何帮助,谢谢!

1 个答案:

答案 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();    
};

http://jsbin.com/doreqa/3/edit?js,console