使用异步管道来轮询 Observables?

mag*_*tic 5 rxjs typescript protractor angular

场景:

我有一个每 2 秒轮询一次 URL 的服务:

export class FooDataService {

...

  public provideFooData() {
    const interval = Observable.interval(2000).startWith(0);
    return interval
      .switchMap(() => this.http.get(this.requestUrl))
      .map(fooData => fooData.json())
  }
}
Run Code Online (Sandbox Code Playgroud)

现在我有一个组件要在其中显示轮询数据:

export class FooComponent implements OnInit {

  constructor(private fooDataService: FooDataService) {}

  public fooData$: Observable<FooData>;

  ngOnInit() {
    this.fooData$ = this.fooDataService.provideFooData();
  }
}
Run Code Online (Sandbox Code Playgroud)

在组件模板中,我将使用异步管道来检索值并将其传递给子组件:

<foo-data-viewer [data]="fooData$ | async"></foo-data-viewer>
Run Code Online (Sandbox Code Playgroud)

这种方法的问题是:

像这样实现它是无法用量角器进行测试的(请参阅我关于此主题本文的最后一个问题)。所有代码都在 ngZone 内执行,量角器将等待所有排队的操作完成后再继续。但Observable.interval()会对无限数量的操作进行排队,从而导致量角器超时。

常见的解决方案:

我最常读到的修复方法是runOutsideAngular这样使用:

export class FooComponent implements OnInit, OnDestroy {

  constructor(private ngZone: NgZone,
              private fooDataService: FooDataService) {}

  public fooData: FooData;
  public fooDataSubscription: Subscription<FooData>;

  ngOnInit() {

    this.ngZone.runOutsideAngular(() => {
      this.fooDataSubscription =
        this.fooDataService.provideFooData()
            .subscribe(
               fooData => this.ngZone.run(() => this.fooData = fooData)
            );
    });
  }

  ngOnDestroy(): void {
    this.fooDataSubscription.unsubscribe();
  }
}
Run Code Online (Sandbox Code Playgroud)

通过在 ngZone 之外运行间隔,Protractor 不会等待轮询完成才继续,因此不会超时。

然而,这意味着:

  • 我无法使用async管道,我必须手动订阅。
  • 我必须手动管理订阅并在组件被销毁时自己清理它。
  • 我无法保持漂亮和干净的 Observable 风格,并且代码变得更加复杂,特别是如果我有多个轮询服务。

我的问题:

有没有办法在 ngZone 之外运行间隔时保持功能性 rxjs 风格并继续使用异步管道(或等效管道)?

我偶然发现了这个 github 项目,它看起来正是我想要的,但我无法让它工作。

我需要的是一个工作示例,用于像在我的场景中一样离开和重新进入该区域,而无需自己管理订阅。

小智 0

import { Injectable } from '@angular/core';
import { interval, of, timer } from 'rxjs';
import { startWith, switchMap, delay, map, share, shareReplay } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class DataService {
  private fakeRequest$ = timer(1000).pipe(map(() => new Date().getTime()))
  private pollingData$ = interval(2000)
    .pipe(
      switchMap((index) => this.fakeRequest$),
      shareReplay(1)
    )
  constructor() { }

  public getData$() {
    return this.pollingData$;
  }
}
Run Code Online (Sandbox Code Playgroud)

斯塔克闪电战