如果单击按钮则跳至下一个值,否则保持计时器活动

时间:2017-08-23 13:12:59

标签: angular rxjs observable

我有以下代码:

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);
}

我有observablearray发出值。 array的时间戳为keysobjects作为值。我将此数组作为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。另外,我认为,我认为这不是最佳方式。我的问题还在于,当我只想取消最近的EventEmitter时,我认为delayWhen()会取消每delayWhen()个。{/ p>

编辑:在大理石图上试用

1 |----X|
2 |------X|
3 |--------------X|
4 |--------------------X|
5 |--------------------------------X|
6 |-------------------------------------------------X|
7 |-X-----X-------------X---X--->
R |-X----XX------------XX---X|

因此,观察者16是基于我上面描述的时间戳的计时器。 7是单击事件发射器(即单击按钮时)。结果是R。正如您所希望看到的那样,点击次数被映射到相应的可观察对象。因此第一个观察者链接到第一次点击等。这导致观察者5和6不会被发射,直到结果Observable由于发出两次点击而结束。

编辑2:Marble

中的轻微错误

我认为大理石有轻微的错误。它并没有说明" normal"之间的时间。应该保留,从而减少Observable的总体存在。

编辑3:添加了Plunker

https://plnkr.co/edit/rV0aDTcSVf4xlnVUJNJN

这是可以玩的Plunker。如果这是有道理的,请告诉我。我从上面的函数中略微修剪了一下(过滤器不适用于Plunker,因为它依赖于其他东西,但认为它有效)。

1 个答案:

答案 0 :(得分:0)

如果有人想知道如何解决这个问题,也许这会给他一个想法。这解决了我的问题:

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)