You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular与RxJS API调用问题:重复触发旧请求数据

问题分析与解决方案

核心问题根源

你的问题出在timesStore.getTimes的实现上:

  1. 你用了共享的BehaviorSubject接收所有API请求结果,导致每次新订阅都会先收到之前缓存的旧值(首次in-home请求的结果),再收到新请求的结果,所以点击virtual按钮时会触发两次回调。
  2. timesStore.getTimes内部直接订阅了timesService,属于RxJS反模式——嵌套订阅不仅会导致流管理混乱,还会让多个请求的结果混入同一个Subject中。
  3. 组件的getTimes方法里用了async修饰catchError的回调,这不符合RxJS的错误处理规范。

修复步骤与代码示例

1. 重构timesStore.getTimes方法

去掉共享Subject,让每次请求返回独立的Observable,避免旧值干扰新请求:

public getTimes(timeRequest: TimesRequest): Observable<TimeResponse | null> {
  return this.timesService.getTimes(timeRequest).pipe(
    map(response => {
      if (response.status === 200) {
        console.log("Time Data received: ", response.body);
        return response.body;
      }
      return null;
    }),
    catchError(error => {
      this.log.error("Error occurred while fetching availability data: ", error);
      return of(null); // 用of返回空结果,避免流中断
    }),
    tap(() => console.log("Observable completed."))
  );
}

2. 修复组件中的getTimes方法

去掉@ts-ignore,用可选链做null检查,修正变量名错误,规范错误处理:

getTimes(timeRequest: TimesRequest, startDate: Date, displayTimeslots: boolean) {
  this.timesStore.getTimes(timeRequest).pipe(
    tap(response => {
      // 用可选链替代null检查,避免@ts-ignore
      if (response?.timeslots) {
        response.timeslots.forEach((ts) => {
          ts.date = new Date(ts.startTime.localTime);
          const options: Intl.DateTimeFormatOptions = {hour: 'numeric', minute: 'numeric', hour12: true};
          ts.displayTime = new Intl.DateTimeFormat('en-US', options).format(ts.date);
        });

        // 修正原代码中的变量名错误:availabilityRequestMount → timeRequest
        if (timeRequest.location === "in-home") {
          this.inHomeTimeslots = response.timeslots;
        } else {
          this.virtualTimeslots = response.timeslots;
        }

        if (displayTimeslots) {
          const displayTimeslotsFilter = timeRequest.location === "in-home" ? this.inHomeTimeslots : this.virtualTimeslots;
          this.displayTimeslots = this.timesStore.findSlots(startDate, displayTimeslotsFilter);
          this.availabilityText = this.displayTimeslots.length 
            ? `Available appointments for ${format(startDate, 'MMM d, yyyy')} (times are approximate)`
            : `No available appointments for ${format(startDate, 'MMM d, yyyy')}`;
        }
      }
    }),
    catchError(error => {
      console.log('Error occurred: ', error);
      return EMPTY; // 返回EMPTY终止错误流,避免订阅报错
    }),
    takeUntil(this.onDestroy$)
  ).subscribe();
}

RxJS最佳实践提示

  • 避免嵌套订阅:所有流操作都通过pipe组合,不要在Observable内部直接调用subscribe。
  • 独立请求用独立Observable:除非需要缓存或全局共享状态,否则不要用单一Subject接收所有请求结果。
  • 类型安全:尽量不要用@ts-ignore,完善TimesRequest和TimeResponse的接口定义,确保属性类型正确。
  • 错误处理:catchError必须返回Observable,不能用async回调,否则会破坏流的结构。

内容的提问来源于stack exchange,提问作者JSchins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 17:53:12