我正在使用RxJs的IntervalObservable每秒轮询一次REST API以获取新的传感器数据。 REST API会以10秒钟内完成的传感器测量值的“存储桶”作为响应,因此来自API的响应也可能包含HTTP标头“ next”,如果有的话,它指向更新的传感器数据存储桶。 >
我当前的实现(见下文)有两个问题:
您对这些混合可观察物有什么建议吗?
export class WidgetService {
private widget: Widget;
private visualizer: any;
private updateScheduler: Subscription;
private timestampOfMostRecentObservation?: number;
constructor(private sensorGateway: SensorGatewayCommunicationService) { }
public initializeVisualization() {
this.visualizer = new TimeSeriesLineChartWithTimeRangeSelector();
this.visualizer.draw(`widget-${this.widget.id}-visualization`, this.widget.name, this.widget.seriesName);
// First update of the visualization with sensor data since a timestamp in the past (121 seconds ago here):
const initialFromTimestamp = Date.now() - 121 * 1000;
this.updateVisualization(initialFromTimestamp);
// Next updates of the visualization are scheduled every second:
this.updateScheduler = IntervalObservable.create(1000)
.subscribe(() => this.updateVisualization(this.timestampOfMostRecentObservation));
}
public destroy() {
this.updateScheduler.unsubscribe();
this.visualizer.destroy();
}
private updateVisualization(fromTimestamp: number) {
const urlForNewObservations = this.widget.sensorMeasurementsUrl + `?from=${fromTimestamp.toString()}`;
this.getSensorObservations(urlForNewObservations)
.pipe(
expand(({sensorObservations, nextUrl}) => { // https://blog.angularindepth.com/rxjs-understanding-expand-a5f8b41a3602
if (sensorObservations && sensorObservations.length > 0 && nextUrl) {
return this.getSensorObservations(nextUrl);
} else {
return empty();
}
}),
concatMap(({sensorObservations}) => sensorObservations),
)
.subscribe((sensorObservations: [number, number][]) => {
const downsampledObservations = this.downsampleSensorObservations(sensorObservations);
this.visualizer.update(downsampledObservations);
});
}
private getSensorObservations(urlForNewObservations: string): Observable<{
sensorObservations: object[],
nextUrl: string | null
}> {
return this.sensorGateway.getApiResource(urlForNewObservations).pipe(
map(response => {
if ('values' in response.body) {
return {
sensorObservations: response.body['values'].map(observation => [
observation[0],
observation[1]
]),
nextUrl: this.getNextLinkUrl(response)
};
} else {
return null;
}
})
);
}
private getNextLinkUrl(response: HttpResponse<object>): string | null {
if (response.headers.has('link')) {
const linkHeader = response.headers.get('link');
/* Example of a linkHeader:
*'</sensors/1/properties/1/observations/20180711/12/19?from=1531311594456>; rel="self",
* </sensors/1/properties/1/observations/20180711/12/18>; rel="prev",
* </sensors/1/properties/1/observations/20180711/12/20>; rel="next"' */
const links = linkHeader.split(',');
const nextLink = links.find(link => link.endsWith('; rel="next"'));
if (nextLink) {
return nextLink.substring(nextLink.indexOf('<') + 1, nextLink.indexOf('>'));
} else {
return null;
}
}
}
}
答案 0 :(得分:1)
我不是使用一个可观察对象的订阅来触发另一个,而是将问题抛在脑后,创建一个可以满足您需要的可观察对象。
我会在您的初始化方法中提出类似的建议:
let fromTimestamp = Date.now() - 121 * 1000;
// Create a base observable, doesn't really matter what it is
this.subscription = of(true).pipe(
// Map to the right call fur the current time
flatMap(() => {
const urlForNewObservations = this.widget.sensorMeasurementsUrl + `?from=${fromTimestamp.toString()}`;
return this.getSensorObservations(urlForNewObservations);
}),
// Repeat the REST call while the sensor returns a next URL:
expand(({sensorObservations, nextUrl}) => { // https://blog.angularindepth.com/rxjs-understanding-expand-a5f8b41a3602
if (sensorObservations && sensorObservations.length > 0 && nextUrl) {
// Set the fromTimestamp for the next set of observations.
fromTimestamp = this.parseTimestamp(nextUrl, fromTimestamp);
return this.getSensorObservations(nextUrl);
} else {
return empty();
}
}),
concatMap(({sensorObservations}) => sensorObservations),
// Keep repeating this
repeat(),
// But wait a second between each one
delay(1000),
// Terminate the whole thing when the service is destroyed / stopped.
takeWhile(() => !this.destroyed)
).subscribe((sensorObservations: [number, number][]) => {
const downsampledObservations = this.downsampleSensorObservations(sensorObservations);
this.visualizer.update(downsampledObservations);
});
您将需要实现parseTimestamp来从URL或类似名称中解析相关的下一个时间戳。
然后实现ngOnDestroy将此this.destroyed设置为true并执行if (this.subscription) this.subscription.unsubscribe();
,这将在服务被销毁时终止订阅-如果要手动控制,请在您的destroy方法中将其手动设置为true /退订