结合 Observable 和 ReplaySubject

Combine Observable with ReplaySubject

在我的项目中,我想使用 Observables 调用一个 api,然后借助 ReplaySubject 的强大功能,我想记住它的结果,这样当我重新访问该页面时,我不必调用再次 api。理想情况下,我会记住一定毫秒数的结果,我知道你可以在 ReplaySubject 中设置。

我知道的一个问题是订阅不能在组件上,因为当您再次访问页面时它会重新启动,因此需要使用服务文件。

首先,我有我的正常 http.service 文件,它应该没问题:

http.service.ts

getVenuesListViaPost(filter, searchObj): Observable<any> {
  return this.http.post("api/venues/search"+filter, searchObj)
}

然后我有了实际显示数据的组件。这样应该没问题。

venues.component.ts

getData(page: number) {
        this.listedAsyncData$ = this.replayApiService.replayVenueApi(page, this.name);
 }

我在我的组件上使用异步管道,即:

venues.component.html

<tr *ngFor="let item of (listedAsyncData$ | async)?.items"><td>{{item.name}}</td></tr>

所以我认为问题出在我的 replayApiService 上,但我很困惑为什么这不起作用。

回放-api.service.ts

import { Injectable } from '@angular/core';
import { HttpService } from "./http.service";
import {ReplaySubject, tap} from 'rxjs';

@Injectable()
export class ReplayApiService {

    private _rememberSource = new ReplaySubject<object>(1, 200000);
    remember$ = this._rememberSource.asObservable();

    constructor(private httpService: HttpService) { }


    replayVenueApi(page, name) {
        this.httpService.getVenuesListViaPost(`?pageno=${page}&pageSize=10`, { name }).pipe(
          tap(res => this._rememberSource.next({
            "items": res.body,
            "totalPages": parseInt(res.headers.get('x-paging-pagecount'))
          }))
        ).subscribe();
      }

}

我可以看到网络调用是正确的,但是页面上没有显示结果。

我建议你重构 replayVenueApi 如下:

replayVenueApi(page, name) {
  this.httpService.getVenuesListViaPost(`?pageno=${page}&pageSize=10`, { name }).pipe(
    tap(res => this._rememberSource.next({
      "items": res.body,
      "totalPages": parseInt(res.headers.get('x-paging-pagecount'))
    }))
  ).subscribe();
}

这不会实现您的“如果少于 20 秒就不要重复请求”,但这确实意味着您的 ReplaySubject 将发出。

在您的组件中,您需要使用 Observable,因为 replayVenuApi 没有 return 任何东西:

getData(page: number) {
  this.listedAsyncData$ = this.replayApiService.remember$;
  this.replayApiService.replayVenueApi(page, this.name);
}

对于计时器元素,一种可能的方法是存储时间戳并将其与每次调用 replayVenueApi()Date.now() 进行比较。如果差异小于 20 秒,则您什么都不做。如果更多,你提出要求。