2015-04-24 45 views
4

我第一次嘗試使用反應式編程,使用,並且發生了令我厭煩的奇怪的交互。我想出了這個事件鏈的工作版本,但是額外的流操作讓它從其他代碼中脫穎而出 - 如你所見。Observable.flatMapLatest:在更深層次取消?

我發現FRP/Bacon.js生成一些非常乾淨的代碼,所以感覺就像我只是缺少一些明顯的操作符。對於這個問題,我們呢?

多個來源建議使用Observable.flatMapLatest來放棄陳舊事件。假設我們要爲輸入流中的每個事件完成IO操作。在下圖中,i代表輸入流事件,rN代表每個請求流的事件,並且xES標記請求流被取消時的時間點。這些很可能是單事件流,所以.意味着事件之後流實際上是關閉的(如果它之前未被取消!)。

   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 +)可能會受到這個嚴重影響。

現在,即使這不是一個真正的問題,它看起來對我的玻璃鋼研究很有價值。如果它是不明確的,現在,這就是我想做的事:

   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上運行,其中difffilter。但不僅如此,它看起來很詭異,它將時間戳/ ID添加爲請求的要求,只是以後我們可以對其進行排序。

我想我的問題是,那麼:我錯過了什麼?有一個更好的方法嗎?我怎樣才能takeUntil每個flatMap事件流(請求),而不是外部的一個(輸入)?

任何幫助表示讚賞,謝謝!

+0

我想大部分的時間'flatMapLatest'是做你想要什麼:如果輸入已經改變,結果以前的請求無效。一個簡單的例子就是搜索字段文本和相應的搜索結果。 – OlliM

+0

在你的例子中,你應該使用'scan'而不是diff來維護已經通過的最大id:如果結果按[r3,r1,r2]順序排列,那麼錯誤的事情將會通過。 – OlliM

回答

0

下面是一個使用最多的ID和掃描解決方案:

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