簡體   English   中英

Rxjs:帶有 takeUntil(timer) 的 Observable 在計時器滴答后繼續發射

[英]Rxjs: Observable with takeUntil(timer) keeps emitting after the timer has ticked

我遇到了takeUntil()一個非常奇怪的行為。 我創建了一個可觀察的計時器:

let finish = Observable.timer(3000);

然后我等了一段時間再打電話

// 2500 ms later
someObservable.takeUntil(finish);

我希望上述 observable 在計時器“滴答”后停止發射,即在其創建后約 500 毫秒。 實際上,它在創建后會持續發射 3000 毫秒,遠遠超過計時器“滴答”的那一刻。 如果我使用包含絕對時間值的 Date 對象創建計時器,則不會發生這種情況。

這是故意的嗎? 如果是,解釋是什么?

這是完整的代碼,可以使用 node.js 運行(它需要npm install rx ):

let {Observable, Subject} = require("rx")
let start = new Date().getTime();
function timeMs() { return new Date().getTime() - start };

function log(name, value) { 
    console.log(timeMs(), name, value);
}

Observable.prototype.log = function(name) {
    this.subscribe( v=>log(name,v), 
                    err=>log(name, "ERROR "+err.message), 
                    ()=>log(name, "DONE"));
    return this;
}

let finish = Observable.timer(3000).log("FINISH");
setTimeout( ()=>Observable.timer(0,500).takeUntil(finish).log("seq"), 2500);

這將生成以下輸出:

2539 'seq' 0
3001 'FINISH' 0
3005 'FINISH' 'DONE'
3007 'seq' 1
3506 'seq' 2
4006 'seq' 3
4505 'seq' 4
5005 'seq' 5
5506 'seq' 6
5507 'seq' 'DONE'

如果我使用絕對時間創建計時器:

let finish = Observable.timer(new Date(Date.now()+3000)).log("FINISH");

然后它按預期運行:

2533 'seq' 0
3000 'seq' 'DONE'
3005 'FINISH' 0
3005 'FINISH' 'DONE'

這種行為在各種情況下似乎相當一致。例如,如果您使用mergeMap()switchMap()創建一個間隔並創建子序列,結果將是相似的:子序列在完成事件之后繼續發射。

想法?

你忘記了冷Observables的第一條規則:每個訂閱都是一個新的流。

您的log操作員有錯誤; 它訂閱Observable一次(從而創建第一個訂閱),然后返回原始Observable ,當您將其傳遞給takeUntil運算符時,該Observable隱式地再次訂閱。 因此,實際上您實際上有兩個seq流處於活動狀態,它們都運行正常。

它適用於絕對情況,因為您基本上將每個流設置為在特定時間發出,而不是訂閱發生的相對時間。

如果您想看到它的工作,我建議您將實現更改為:

let start = new Date().getTime();
function timeMs() { return new Date().getTime() - start };

function log(name, value) { 
    console.log(timeMs(), name, value);
}

Observable.prototype.log = function(name) {
    // Use do instead of subscribe since this continues the chain
    // without directly subscribing.
    return this.do(
      v=>log(name,v), 
      err=>log(name, "ERROR "+err.message), 
      ()=>log(name, "DONE")
    );
}

let finish = Observable.timer(3000).log("FINISH");

setTimeout(()=> 
  Observable.timer(0,500)
    .takeUntil(finish)
    .log("seq")
    .subscribe(), 
2500);

為了完整起見,這里是實際執行我想要的代碼。 通過使用Observable.publish().connect()它創建了一個“熱”計時器,立即開始計時,並為所有訂閱者保持相同的時間。 正如@paulpdaniels 所建議的那樣,它還可以避免在“日志”方法中進行不需要的訂閱。

警告:注意競爭條件。 如果子序列在計時器計時后開始,它將永遠不會停止。 為了演示,將最后一行中的超時從 2500 更改為 3500。

let {Observable, Subject, Scheduler, Observer} = require("rx")
let start = new Date().getTime();
function timeMs() { return new Date().getTime() - start };

function log(name, value) { 
    console.log(timeMs(), name, value);
}

var logObserver =  function(name) {
    return Observer.create( 
      v=>log(name,v), 
      err=>log(name, "ERROR "+err.message), 
      ()=>log(name, "DONE"));
}

Observable.prototype.log = function(name) { return this.do(logObserver(name)); }

Observable.prototype.start = function() { 
    var hot = this.publish(); hot.connect(); 
    return hot; 
}

let finish = Observable.timer(3000).log("FINISH").start();

setTimeout(()=> 
  Observable.timer(0,500)
    .takeUntil(finish)
    .log("seq")
    .subscribe(), 
2500);

輸出是

2549 'seq' 0
3002 'FINISH' 0
3006 'seq' 'DONE'
3011 'FINISH' 'DONE'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM