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

Angular中RxJS取消旧HTTP请求失效,多次打印结果问题排查

问题描述

尝试实现Angular搜索时自动取消旧HTTP请求的功能,但功能失效——输入搜索词时,控制台会多次打印“result gotten”,旧请求的结果依然会返回并更新状态。

相关代码

模板代码

<input id="searchbox" value="{{ searchTerm$ | async }}" #searchBox type="text" (input)="search(searchBox.value)">

组件.ts代码

private searchTerms = new Subject<string>();

ngOnInit() {
    this.searchTerms.pipe(
        tap((term: string) => this.store.dispatch(new SearchForTermAction(term))),
    ).subscribe();
}

search(term: string): void {
    if (term.length >= 2) {
      this.searchTerms.next(term);
    }
}

状态管理Action代码

@Action(SearchForTermAction)
async searchForTerm(ctx: StateContext<SearchStateModel>, action: SearchForTermAction) {
    const currentState = ctx.getState();
    of(action.searchTerm).pipe(
        debounceTime(500),
        // ignore new term if same as previous term
        distinctUntilChanged(),
        switchMap(term => this.service.searchTerm(term)),
        ).subscribe(res => {
          console.log("result gotten");
          ctx.setState({
            searchResults: res,
            loading: false,
          });
        });
      }
错误原因
  1. switchMap未作用于统一流:每次触发SearchForTermAction时,都会新建一个独立的of(action.searchTerm)流并订阅。这些流相互独立,switchMap只能取消当前流内的请求,无法跨Action实例取消之前发起的请求。
  2. 防抖去重逻辑位置错误:防抖和去重放在Action内部,导致每个符合条件的输入都会开启一个500ms延迟的独立任务,多个任务到期后都会发起请求并返回结果。
  3. 未清理订阅:每次触发Action都会新增一个订阅,没有对这些订阅进行管理,旧请求的订阅依然会在结果返回时执行状态更新和日志打印。
修正方案

正确的做法是将防抖、去重和switchMap逻辑移到组件的searchTerms流中,让整个搜索流程成为一个连续的Observable链,确保switchMap能正确取消之前的请求:

1. 修改组件.ts代码

private searchTerms = new Subject<string>();
private searchSubscription?: Subscription;

ngOnInit() {
    this.searchSubscription = this.searchTerms.pipe(
        debounceTime(500),
        distinctUntilChanged(),
        switchMap((term: string) => this.service.searchTerm(term))
    ).subscribe(res => {
        console.log("result gotten");
        this.store.dispatch(new UpdateSearchResultsAction(res));
    });
}

search(term: string): void {
    if (term.length >= 2) {
      this.searchTerms.next(term);
    }
}

ngOnDestroy() {
    this.searchSubscription?.unsubscribe();
}

2. 调整状态管理逻辑

拆分Action,让Action只负责状态更新:

// 新增更新搜索结果的Action
export class UpdateSearchResultsAction {
  static readonly type = '[Search] Update Results';
  constructor(public searchResults: any) {}
}

@Action(UpdateSearchResultsAction)
updateSearchResults(ctx: StateContext<SearchStateModel>, action: UpdateSearchResultsAction) {
    ctx.setState({
        ...ctx.getState(),
        searchResults: action.searchResults,
        loading: false,
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 16:38:17