Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Angular 如何使用RxJs定期抓取REST API?_Angular_Typescript_Rxjs - Fatal编程技术网

Angular 如何使用RxJs定期抓取REST API?

Angular 如何使用RxJs定期抓取REST API?,angular,typescript,rxjs,Angular,Typescript,Rxjs,我正在使用RxJs的IntervalObservable每秒轮询一个RESTAPI以获取新的传感器数据。REST API在10秒内以传感器测量的“桶”进行响应,因此来自API的响应也可能包含一个HTTP头“下一个”,该头指向一个最近的传感器数据桶(如果有) 我当前的实施(见下文)存在两个问题: 传感器数据提取从过去的时间戳开始,这可能意味着提取大量数据桶。如果用户离开使用此服务的Angular组件,它将销毁该服务,但初始数据获取将继续。我觉得我应该在updateVisualization()方

我正在使用RxJs的IntervalObservable每秒轮询一个RESTAPI以获取新的传感器数据。REST API在10秒内以传感器测量的“桶”进行响应,因此来自API的响应也可能包含一个HTTP头“下一个”,该头指向一个最近的传感器数据桶(如果有)

我当前的实施(见下文)存在两个问题:

  • 传感器数据提取从过去的时间戳开始,这可能意味着提取大量数据桶。如果用户离开使用此服务的Angular组件,它将销毁该服务,但初始数据获取将继续。我觉得我应该在updateVisualization()方法中跟踪getSensorObservations()返回的Observable,并在销毁服务时取消订阅,但这意味着,每当我开始使用IntervalObservable调用updateVisualization()时,我将拥有“一个(间隔)Observable中的Observable”我要把两个都杀了。我认为这不是最好的办法
  • 初始updateVisualization()可能需要一些时间来获取所有必要的历史数据,但是下一个updateVisualization()调用将使用IntervalObservable启动,而无需等待初始调用
你对这些混合观测有什么建议吗

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://ncjamieson.com/understanding-expand/
          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;
      }
    }
  }
}
导出类WidgetService{
私有小部件:小部件;
私人可视化工具:任何;
私有更新计划器:订阅;
最新观测的私有时间:编号;
构造函数(专用传感器网关:传感器网关通信服务){}
公共初始化{
this.visualizer=新的TimeSeriesLineChartWithTimeRangeSelector();
this.visualizer.draw(`widget-${this.widget.id}-visualization`,this.widget.name,this.widget.seriesName);
//自过去时间戳(此处为121秒前)以来,首次使用传感器数据更新可视化:
const initialFromTimestamp=Date.now()-121*1000;
此.updateVisualization(initialFromTimestamp);
//可视化的下一次更新计划为每秒更新一次:
this.updateScheduler=IntervalObservable.create(1000)
.subscribe(()=>this.updateVisualization(this.timestampOfMosterecentobservation));
}
公共销毁(){
this.updateScheduler.unsubscribe();
this.visualizer.destroy();
}
私有updateVisualization(fromTimestamp:number){
const urlForNewObservations=this.widget.sensormeasurementsrl+`?from=${fromTimestamp.toString()}`;
此.GetSensor观察(URFORNEW观察)
.烟斗(
展开({sensorObservations,nextUrl})=>{//https://ncjamieson.com/understanding-expand/
if(sensorObservations&&sensorObservations.length>0&&nextUrl){
返回此.getSensorObservations(nextUrl);
}否则{
返回空();
}
}),
concatMap(({sensorObservations})=>sensorObservations),
)
.订阅((sensorObservations:[编号,编号][])=>{
const downsampledObservations=此。DownsamplesSensorobervations(传感器观测);
this.visualizer.update(下采样观察);
});
}
私有getSensorObservations(urlForNewObservations:string):可观察{
返回此.sensorGateway.getapirource(urlForNewObservations).pipe(
映射(响应=>{
if('values'在response.body中){
返回{
sensorObservations:response.body['values'].map(观察=>[
观察[0],
观察[1]
]),
nextUrl:this.getNextLinkUrl(响应)
};
}否则{
返回null;
}
})
);
}
私有getNextLinkUrl(响应:HttpResponse):字符串| null{
if(response.headers.has('link')){
const linkHeader=response.headers.get('link');
/*链接头的示例:
*“;rel=“self”,
*;rel=“prev”,
*;rel=“下一步”*/
const links=linkHeader.split(',');
const nextLink=links.find(link=>link.endsWith(';rel=“next”);
如果(下一个链接){
返回nextLink.substring(nextLink.indexOf(“”));
}否则{
返回null;
}
}
}
}

我不会使用订阅一个可观察对象来触发另一个可观察对象,而是将问题抛到脑后,创建一个可观察对象,它可以满足您的需要

我会在你的初始化方法中提出类似的建议:

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://ncjamieson.com/understanding-expand/

      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或类似文件中的相关下一个时间戳


然后实现Ngondestory将this.destroy设置为true,并执行
if(this.subscription)this.subscription.unsubscripte(),它将在服务被销毁时终止订阅-如果要手动控制,请在销毁方法中将其手动设置为true/unsubscribe。

TLDR;2) 您可以首先调用服务来获取历史thata,然后
切换映射到interval observable。这样间隔将在初始数据获取后生效。谢谢!这种管理API调用的方法解决了问题中提到的两个问题。太好了,很高兴听到@SanderVandenHautte-我发现对于observable,正确的答案几乎总是“尝试创建一个做正确事情的单一observable”。我注意到我的答案没有正确设置“fromTimestamp”,我相信你已经解决了这个问题?是的,我已经在flatMap()中设置了URL和fromTimestamp(使用这个.timestampOfMosterRecentObservation)。然而,我很快就大喊胜利,我的第一个测试是使用fromTimestamp返回一个空的传感器数据桶。稍早放置fromTimestamp会导致对同一URL(和同一fromTimestamp)的大量连续调用。我在getSensorObservations()方法内的管道中设置this.timestampofMosterRecentObservation()的值,紧随其后