RxJS中race操作符重复触发问题:输入组件搜索重复执行排查
问题分析与解决方案
在输入组件中,输入内容后约100ms按下回车键,会触发两次搜索:一次是回车触发的即时搜索,另一次是500ms防抖时间结束后的搜索。
核心问题
你忽略了按下回车键时,已经进入防抖等待队列的输入事件并没有被终止。当输入内容后,inputChanged$发出的数据流进入debounceTime(500)管道等待,此时按下回车触发enterPressed$执行搜索,但防抖队列里的事件仍会在500ms后执行,导致重复搜索。
解决方案
可以通过以下方式修正,核心逻辑是在回车触发时终止当前防抖等待的输入流:
import 'zone.js/dist/zone'; import { Component, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { bootstrapApplication } from '@angular/platform-browser'; import { Subject, merge, takeUntil, debounceTime } from 'rxjs'; @Component({ selector: 'my-app', standalone: true, imports: [CommonModule], template: ` <input type="search" placeholder="Search" (input)="inputChanged($any($event.target).value)" (keydown.enter)="enterPressed($any($event.target).value)"/> <ul> <li *ngFor="let message of messages">{{message}}</li> </ul> `, }) export class App implements OnDestroy { messages: string[] = []; inputChanged$ = new Subject<string>(); enterPressed$ = new Subject<string>(); destroy$ = new Subject<void>(); constructor() { // 合并输入防抖流和回车触发流,同时确保回车时终止防抖等待 merge( this.inputChanged$.pipe( debounceTime(500), // 一旦回车触发,终止当前防抖流 takeUntil(this.enterPressed$) ), this.enterPressed$ ) .pipe(takeUntil(this.destroy$)) .subscribe(value => { const displayText = typeof value === 'string' && value.startsWith('Enter') ? value : `Debounced input with value: ${value}`; this.messages.push(displayText); }); } inputChanged(targetValue: string) { this.inputChanged$.next(targetValue); } enterPressed(targetValue: string) { this.enterPressed$.next(`Enter pressed with value: ${targetValue}`); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } } bootstrapApplication(App);
关键逻辑说明
- 使用
takeUntil(this.enterPressed$),在回车键触发时,立即终止当前处于debounceTime等待中的输入流,避免其后续触发搜索。 - 合并防抖流与回车流,统一处理两种触发场景,确保每次只会执行一次搜索。
内容的提问来源于stack exchange,提问作者bartosz.baczek
相关产品推荐
相关产品推荐

