创建一个只读取一次值并多次返回的observable的正确方法

时间:2018-01-19 22:50:25

标签: angular typescript rxjs observable angular2-observables

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

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

目前代码如下:

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方法返回HTTP请求的observable(由EnvironmentConfigService.read返回),当它正在进行时;一旦读取了数据,它就会变为使用Observable.of创建的静态observable。

我主要担心的是:

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

3 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:1)

1)您可以继续使用http observable。做一些像:

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

.publishReplay(1) - &gt;缓存最近的值。

.refCount() - &gt;只要有订阅者,就会保持可观察性。

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

答案 2 :(得分:1)

您可以使用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();
  }
}