创建一次读取值并 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
.
创建的静态可观察对象
我主要担心的是:
- 我是否需要切换到使用静态可观察对象,或者我是否可以继续使用来自 HTTP 请求的可观察对象(我不希望在后续
subscribe
调用中重新执行 HTTP 请求).
- 如果需要静态 observable,是否需要取消订阅 HTTP observable?
- 有没有标准的方法来做这种事情?
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 或更高版本,因为有一个重要的错误修复。
我有一个 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
.
我主要担心的是:
- 我是否需要切换到使用静态可观察对象,或者我是否可以继续使用来自 HTTP 请求的可观察对象(我不希望在后续
subscribe
调用中重新执行 HTTP 请求). - 如果需要静态 observable,是否需要取消订阅 HTTP observable?
- 有没有标准的方法来做这种事情?
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 或更高版本,因为有一个重要的错误修复。