我有以下代码:
startReplay(replayValues: Object) {
this.replayRunning = true;
var firstTs: number;
const repObs = Observable.from(Object.keys(replayValues)).filter(key => Object.keys(diff(replayValues[key], this.initialFormObject)).length > 0);
repObs.first().subscribe(key => firstTs = Number(key));
return repObs
.delayWhen(key => Observable.timer(Number(key) - firstTs))
.map(key => replayValues[key])
.finally(() => this.replayRunning = false)
.takeWhile(() => this.replayRunning === true);
}
我有从array
发出值的observable
.array
将时间戳作为keys
,objects
作为值。我以observable
运行此数组并在其他地方订阅(让我们假设打印到控制台(。
如您所见,observable
在 x (=Number(key) - firstTs
( 秒后发出一个值。之后,我映射它,只发出对象,不再关心时间戳。我想运行它,直到replayRunning
的值true
.
现在,我的app.component.html
中有一个按钮,用于调用代码中skipReplayStep() {}
的当前空函数。现在,我希望有可能停止delayWhen()
并立即前进到下一步,即map(key =>...)
已启动。
初步想法(但不是实际解决方案(
我想到了merge
+first()
,其中Observable.timer(Number(key) - firstTs)
部分与EventEmitter
合并。但我无法将EventEmitter
变成Observable
.另外,我认为这不是最好的方法。我的问题也是,我认为当我只想取消最近的delayWhen()
时,EventEmitter
会取消每delayWhen()
。
编辑:大理石图审判
1 |----X|
2 |------X|
3 |--------------X|
4 |--------------------X|
5 |--------------------------------X|
6 |-------------------------------------------------X|
7 |-X-----X-------------X---X--->
R |-X----XX------------XX---X|
因此,1
到6
的观察者是基于我上面描述的时间戳的计时器。7
是单击事件发射器(即单击按钮时(。 结果R
。如您所愿,点击被映射到相应的可观察对象。因此,第一个观察者链接到第一次点击,依此类推。这导致观察点 5 和 6 在结果Observable
完成之前不会发出,因为发出了两次点击。
编辑2:大理石中的轻微错误
我认为大理石有一点错误。它没有考虑到应该保持"正常"之间的时间,从而减少Observable
的整体存在。
编辑3:添加了普伦克
https://plnkr.co/edit/rV0aDTcSVf4xlnVUJNJN
这是可以玩的 Plunker。让我知道这是否有意义。我从上面的函数中对其进行了一些修剪(过滤器不适用于 Plunker,因为它依赖于其他东西,但认为它可以工作(。
如果有人想知道如何解决这个问题,也许这给了他一个想法。这解决了我的问题:
startReplay(replayValues: Object) {
this.replayRunning = true;
const obsRepValues = Observable
.from(Object.keys(replayValues))
.filter(key => Object.keys(diff(replayValues[key], this.initialFormObject)).length > 0)
const obsTimestamps = obsRepValues.map((x, i) => Number(x));
const obsValues = obsRepValues.map((x, i) => replayValues[x]);
const obsIndex = obsRepValues.map((x, i) => i + 1);
const obsTimeDiff = obsRepValues
.pairwise()
.map(key => Math.ceil((Number(key[1]) - Number(key[0])) / 100) * 100)
.startWith(0);
const obsTimeElapsed = obsTimeDiff.scan((acc, curr) => acc + curr, 0);
const obsResult = Observable.zip(obsIndex, obsTimestamps, obsValues, obsTimeDiff, obsTimeElapsed);
const obsTimer = obsResult;
return obsResult
.delayWhen(x =>
Observable.race(
Observable.interval(10).skipWhile(() => x[0] != this.currentReplayStep), //BehaviourSubject + skip(x)
Observable.interval(10).skipWhile(() => (x[0] - 1) != this.currentReplayStep).delay(x[3]) //BehaviourSubject + skip(x - 1) + delay
)
)
.takeWhile(() => this.replayRunning === true)
.do(x => {
this.currentReplayStep = x[0];
const interval: number = 100;
obsTimeDiff
.skipWhile(() => x[0] != this.currentReplayStep)
.elementAt(x[0], 0)
.switchMap(duration => Observable.timer(0, interval).mapTo(-interval).scan((acc, curr) => acc + curr, duration).map(x => x / 1000))
.takeWhile(() => this.replayRunning === true && x[0] == this.currentReplayStep)
.finally(() => this.timerValue = 0)
.subscribe(y => this.timerValue = y);
})
.map(x => x[2])
.finally(() => this.stopReplay());
}
skipToNext() {
this.currentReplayStep = this.currentReplayStep + 1;
}
我目前正在研究一种使用BehaviorSubject
的解决方案,该会发出而不是obsResult
。从理论上讲,这并不太难。我缺少一个发出与"上一个"Observable
相同位置的运算符(或者可能不知道它(。伪代码样式:
BehaviorSubject.race(2 Observables).WantedOperator(emittingNewObservable)