angular - 使用异步管道轮询 Observables?

标签 angular typescript protractor rxjs

场景:

我有一个每 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())
  }
}

现在我有一个组件,我想在其中显示此轮询数据:
export class FooComponent implements OnInit {

  constructor(private fooDataService: FooDataService) {}

  public fooData$: Observable<FooData>;

  ngOnInit() {
    this.fooData$ = this.fooDataService.provideFooData();
  }
}

在组件模板中,我将使用异步管道来检索值并将其传递给子组件:
<foo-data-viewer [data]="fooData$ | async"></foo-data-viewer>

这种方法的问题:

像这样实现它不能用 Protractor 测试(参见我的 last question on this topicthis article )。所有代码都在 ngZone 内执行, Protractor 将在继续之前等待所有排队的 Action 完成。但是Observable.interval()将无限数量的 Action 排队,从而导致 Protractor 超时。

常见的解决方法:

我最常阅读的修复方法是使用 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();
  }
}

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

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

  • 我的问题:

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

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

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

    最佳答案

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

    Stackblitz

    关于angular - 使用异步管道轮询 Observables?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43388701/

    相关文章:

    angular - 如何在自定义元素上实现 ngModel?

    angular - FormGroup 上的 ValueChanges 在 KeyUp 和 onBlur 上触发

    typescript - ES6 样式导入语句在 TypeScript 中不起作用

    javascript - Typescript 和 Browserify - 未捕获的 TypeError : Cannot read property 'step' of undefined

    angularjs - 在 Protractor 中检索中继器内的元素

    angularjs - 在套件运行期间努力保持对表格单元格的引用

    forms - 如何在 formArray 中设置值

    javascript - Angular 右键单击也打开右侧浏览器菜单

    javascript - 如何与异步 JavaScript 生成器进行双向通信?

    javascript - 单击 Protractor 中的可见按钮?