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, }); }); }
错误原因
switchMap未作用于统一流:每次触发SearchForTermAction时,都会新建一个独立的of(action.searchTerm)流并订阅。这些流相互独立,switchMap只能取消当前流内的请求,无法跨Action实例取消之前发起的请求。- 防抖去重逻辑位置错误:防抖和去重放在Action内部,导致每个符合条件的输入都会开启一个500ms延迟的独立任务,多个任务到期后都会发起请求并返回结果。
- 未清理订阅:每次触发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
相关产品推荐
相关产品推荐

