RxJS 6 当页面不活动时暂停或缓冲可观察

Dan*_*cal 3 asynchronous rxjs typescript angular rxjs6

所以我有一个流,比如说字母,我需要按正确的顺序将所有字母组合成一个词。一切工作正常,直到用户更改标签,尽量减少浏览器或切换应用程序-行为几乎是一样的,如果我用setTimeout()-搞砸的顺序,丢失物品等我尝试用达到我的目的bufferWhen()bufferToggle()takeUntil()publish()connect(),但没有成功了。我也考虑过使用delayWhen,但它已被弃用并且可能不适合,因为它会立即停止流。我应该使用哪些功能以及如何使用?这是我的代码:

export class MyComponent implements AfterViewInit {
  private visibilityChange$ = fromEvent(document, 'visibilitychange').pipe(startWith('visible'), shareReplay({ refCount: true, bufferSize: 1 }));
  private show$ = this.visibilityChange$.pipe(filter(() => document.visibilityState === 'visible'));
  private hide$ = this.visibilityChange$.pipe(filter(() => document.visibilityState === 'hidden'));

  public ngAfterViewInit() {
    const lettersStream$ = zip( // add delay for each letter
            from(['w', 'o', 'r', 'd']),
            interval(1000))
           // pause when hide$ fires, resume when show$
          .pipe(map(([letter, delayTime]) => letter))
          .subscribe(console.log);
  }
}
Run Code Online (Sandbox Code Playgroud)

我在StackBlitz上做了一个演示- 我想要的只是看(在选项卡处于非活动状态时停止写作)该短语如何写在屏幕上。

Gog*_*eli 6

由于我在我的RxJS Snake Game 中做了类似的暂停/取消暂停的事情,我会用你的例子来帮助你。

理念就是要有一个interval(1000)作为真理的来源,这意味着一切都将基于它。所以我们的目标是让这个时间间隔可暂停,基于我们需要停止在可见性隐藏上发出事件并继续可见性显示的事实。最后,为了让事情变得更简单,我们可以在可见性隐藏时停止收听源间隔,并在可见性显示到达时重新开始收听。现在让我们进入确切的实现:

您也可以在RxJS Pause Observable 中使用修改后的 StackBlitz 演示代码。

import { of, interval, fromEvent, timer, from, zip, never } from 'rxjs';
import { delayWhen, tap, withLatestFrom, concatMap, take, startWith, distinctUntilChanged, switchMap, shareReplay, filter, map, finalize } from 'rxjs/operators';

console.log('-------------------------------------- STARTING ----------------------------')

class MyComponent {
  private visibilityChange$ = fromEvent(document, 'visibilitychange')
    .pipe(
      map(x => document.visibilityState),
      startWith('visible'),
      shareReplay(1)
    );

  private isVisible$ = this.visibilityChange$.pipe(
    map(x => x === 'visible'),
    distinctUntilChanged(),
  );

  constructor() {
    const intervalTime = 1000;
    const source$ = from('word or two'.split(''));
    /** should remove these .pipe(
        concatMap(ch => interval(intervalTime).pipe(map(_ => ch), take(1)))
      );*/

    const pausableInterval$ = this.isVisible$.pipe(
      switchMap(visible => visible ? interval(intervalTime) : never()),
    )

    const lettersStream$ = zip(pausableInterval$, source$).pipe(
      map(([tick, letter]) => letter),
    ).subscribe(letter => {
      this.writeLetter(letter);
    });
  }

  private writeLetter(letter: string) {
    if (letter === ' ') letter = '\u00A0'; // fix for spaces
    document.body.innerText += letter;
  }
}

const component = new MyComponent();
Run Code Online (Sandbox Code Playgroud)

这是来自 StackBlitz 的确切代码,我复制到这里是为了更好地为您解释。

现在让我们为您分解有趣的部分:

  1. 看看visibilityChange$isVisible$。它们稍作修改,以便第一个发出字符串值'visible''hidden'基于document.visibilityState. 当document.visibilityStateequals时,第二个发出 true 'visible'

  2. 看看source$。这将发出一个字母,然后等待1秒的帮助下concatMap,并intervaltake(1)做这个过程,直到没有留在文本字符。

  3. 看看pausableInterval$。基于this.isVisible$哪个会根据 发生变化document.visibilityState,我们pausableInterval$将每秒发射项目或根本不会发射任何东西never()

  4. 最后看看lettersStream$。在 的帮助下zip(),我们将压缩pausableInterval$source$,因此我们将获得来自来源的一封信和来自可暂停间隔的一个勾号。如果pausableInterval$由于可见性改变而停止发射,zip 也会等待,因为它需要两个 Observable 一起发射以向订阅发送事件。