[英]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.