创建一次读取值并 returns 多次读取值的可观察对象的正确方法

Proper way to create an observable that reads a value once and returns it many times

我有一个 Angular 应用程序,其中包含一个使用 HTTP (EnvironmentConfigService) 读取运行时配置的服务,以及另一个使用该服务的数据的服务 (UrlManagerService)计算各种服务的 URL 并将它们提供给应用程序的其余部分。

我希望它的行为方式是:UrlManagerService 在应用程序启动时读取配置,并且它提供调用者用来通过可观察对象获取计算的 URL 的方法。如果 HTTP 请求仍在进行中,则可观察对象会在请求完成时发出;如果请求已经完成,可观察对象会立即发出内存中的数据。

代码目前看起来像这样:

import { Injectable } from '@angular/core';
import { EnvironmentConfigService } from './environment-config.service';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class UrlManagerService {
    // it has public properties for all of the URLs

    private configLoaded: Observable<UrlManagerService>;

    constructor(private configService: EnvironmentConfigService) {
        this.configLoaded = this.configService
            .read()
            .map(config => {
                // compute URLs and assign to properties
                return this;
            });
        this.configLoaded.subscribe(() => {
            this.configLoaded = Observable.of(this)
        });
    }

    public getInitialized(): Observable<UrlManagerService> {
        return this.configLoaded;
    }
}

getInitialized 方法 returns HTTP 请求(由 EnvironmentConfigService.read 返回)在进行时的可观察对象;读取数据后,它会更改为使用使用 Observable.of.

创建的静态可观察对象

我主要担心的是:

  1. 我是否需要切换到使用静态可观察对象,或者我是否可以继续使用来自 HTTP 请求的可观察对象(我不希望在后续 subscribe 调用中重新执行 HTTP 请求).
  2. 如果需要静态 observable,是否需要取消订阅 HTTP observable?
  3. 有没有标准的方法来做这种事情?

1) 您可以继续使用 http observable。只需执行以下操作:

this.configLoaded = this.configService
        .read()
        .map(config => {
            // compute URLs and assign to properties
            return this;
        })
        .publishReplay(1)
        .refCount();

.publishReplay(1) -> 缓存最新值。

.refCount() -> 只要有订阅者就保持可观察。

2) http 请求完成后,它会自行完成,因此无需取消订阅。

您可以使用 AsyncSubject,请参阅 What's the point of AsyncSubject in RXJS

@Injectable()
export class UrlManagerService {

  private configLoaded = new AsyncSubject<UrlManagerService>();

  constructor(private configService: EnvironmentConfigService) {
    this.configService
      .read()
      .map(config => {
        // compute URLs and assign to properties
        return this;
      })
      .subscribe(this.configLoaded) // pass the result and the 'complete' notification
  }

  public getInitialized(): Observable<UrlManagerService> {
    return this.configLoaded.asObservable();
  }
}

您可以使用 shareReplay(1) 通过 ReplaySubject 多播可观察对象。与 publishReplay(1).refCount() 不同,即使在某个时刻没有订阅者,这也会保留缓存。它仍然可以在出错时重试,这使得它非常适合这个。

return this.configService.read()
  .shareReplay(1);

但是,您需要 rxjs 5.5.0 或更高版本,因为有一个重要的错误修复。