Rxjs:带有takeUntil(定时器)的Observable在定时器勾选后继续发光

时间:2017-01-08 03:06:52

标签: javascript rxjs

我遇到了takeUntil()的一种非常奇怪的行为。我创建了一个可观察的计时器:

let finish = Observable.timer(3000);

然后我等待一段时间并致电

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

我希望所述观察者在定时器"滴答"之后停止发光,即在其创建后约500ms。实际上它在创建后持续发出3000ms ,超出了计时器"滴答作响的时刻。如果我使用包含绝对时间值的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()获取间隔并创建子序列,结果将类似:子序列继续超出完成事件。

思想?

2 个答案:

答案 0 :(得分:3)

您忘记了第一个冷Observables规则:每个订阅都是一个新流。

您的log运营商有错误;它正在订阅一次Observable(从而创建第一个订阅),然后返回原始Observable,当您将其传递给时,它会隐式地再次订阅 takeUntil运营商。因此实际上你实际上有两个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);

答案 1 :(得分:0)

为了完整起见,这里的代码实际上是我想要的。通过使用Observable.publish().connect(),它会创建一个" hot"立即开始计时的计时器,并为所有订户保持相同的时间。它还可以避免" log"中的不需要的订阅。方法,如@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'