Angular-多次订阅而不触发多个呼叫?

时间:2019-08-06 11:26:53

标签: angular rxjs

我有一个服务呼叫,其响应被缓存在我的Angular服务中,如下所示:

public cacheMyServiceResponse(): Observable<any> {
  return this.appConfig.getEndpoint('myService')
    .pipe(
      switchMap((endpoint: Endpoint) => this.http.get(endpoint.toUrl())),
      tap((body: any) => {
        if (body) { //  this endpoint can also return a 204 (No Content), where body is null
          this.cache = body.myData;
        }
      }),
      take(1),
      catchError(error => {
        this.errorService.trackError(error.status);
        return of(true);
      })
    );
}

因此,我的http.get调用的响应将存储在一个名为“ cache”的全局变量中。

问题是,此调用实际上可能响应得很晚,因此我们希望在页面加载时(初始化时)立即调用此端点。 但是实际的响应,或者我们的呼叫是否结束(成功还是错误),只有在用户单击按钮时才需要此信息。当然,在单击按钮的那一刻,响应可能尚未出现,并且在这种情况下,我要等待。 (因此,我需要的不仅仅是一个简单的布尔标志)

所以我想像这样在ngOnInit中初始化此调用:

ngOnInit() {
    this.myService.cacheMyServiceResponse().subscribe();
}

但是在其他地方,我需要知道它是否已经结束通话,而无需两次触发http通话。

onClick() {
    this.myService.cacheMyServiceResponse().subscribe(() => {
       // call is finished..
    });
}

目前,该服务将被调用两次。我该怎么办?

PS:我没有故意进行错误处理,我只需要知道服务调用是否全部完成即可。

5 个答案:

答案 0 :(得分:4)

我建议改为使用ReplaySubject()并订阅ReplaySubject() onClick,它会等待您的服务在仍可以订阅的同时发出数据,即使该服务未在订阅之前被订阅服务发出的数据,您将不会丢失数据:

yourWaitingData = new ReplaySubject();
subscription;

ngOnInit() {
    this.myService.cacheMyServiceResponse().subscribe(res => {
        //yourWaitingData only emit when res is return from API call
        this.yourWaitingData.next(res)
    });
}

然后订阅它:

onClick() {
    if(this.subscription){
       this.subscription.unsubscribe()
    }
    this.subscription = this.yourWaitingData.subscribe((x) => {
       // subscribed and will wait for data to be emited from service
       console.log(x)
    });
}

答案 1 :(得分:1)

您可以在此处为场景使用Resolvers。当您点击路线时,它将调用您的方法。

示例:

@Injectable()
export class ExampleResolver implements Resolve<any> {
  constructor(private apiService: APIService) {}

  resolve(route: ActivatedRouteSnapshot) {
    return this.apiService.getItems(route.params.date);
  }
}

您的路线:

{
  path: 'routeName',
  component: YourComponent,
  resolve: { items: ExampleResolver }
}

答案 2 :(得分:0)

使用布尔变量检查实际响应的状态,或者我们的调用是否完成(成功还是错误);然后可以在用户单击按钮时检查此布尔值...以下代码解释我的意思...

  callMade:boolean = false;
  callFinished:boolean = false;

  ngOnInit(){
    this.callMade = true;
    this.myService.cacheMyServiceResponse().subscribe(
          dataa => { /* get and process data */}
          ,() => { /*this is the finally block */
          this.callFinished = true;
          }
    );
  }

  someOtherFunction(){
    if (this.callMade == true && this.callFinished == false){
      /* call the service again */
    }
  }

答案 3 :(得分:0)

为什么不保存Observable?

public cacheMyServiceResponse(): Observable<any> {
  if(this.cache) {
      return of(this.cache);
  else if(!this.currentCall) {
    this.currentCall = this.appConfig.getEndpoint('myService')
      .pipe(
        switchMap((endpoint: Endpoint) => this.http.get(endpoint.toUrl())),
        tap((body: any) => {
          if (body) { //  this endpoint can also return a 204 (No Content), where body is null
            this.cache = body.myData;
          }
        }),
        take(1),
        catchError(error => {
          this.errorService.trackError(error.status);
          return of(true);
        })
      );
  }
  return this.currentCall;
}

答案 4 :(得分:0)

使用shareReplay创建一个简单的缓存,该缓存将触发http请求一次,并将其返回值提供给缓存中的所有后续订阅。

服务

private cache$: Observable<any>;

getData(): Observable<any> {
  if (!this.cache$) {
    this.cache$ = this.requestData().pipe(shareReplay(1));
  }
  return this.cache$;
}

private requestData(): Observable<any> {
  return this.appConfig.getEndpoint('myService')
    .pipe(
      switchMap((endpoint: Endpoint) => this.http.get(endpoint.toUrl())),
      catchError(error => {
        this.errorService.trackError(error.status);
        return of(true);
      })
    );
}

组件

您可以多次订阅this.myService.getData(),而无需触发多个http呼叫。只有第一个订阅会触发呼叫。

ngOnInit() {
  this.myService.getData().subscribe();
}

onClick() {
  this.myService.getData().subscribe(data => console.log('data from click'));
}

https://stackblitz.com/edit/angular-ysafwb