Angular实现函数防抖:500ms内重复调用终止前次执行
Angular实现搜索防抖(仅保留最后一次请求)
方案一:使用RxJS Subject + debounceTime(推荐,符合响应式编程风格)
原代码存在两个问题:一是输入时频繁触发请求,二是_initializeProjectList中获取uuid的异步逻辑可能导致dispatch时uuid为空。以下是优化后的防抖实现:
import { Subject } from 'rxjs'; import { debounceTime, takeUntil, switchMap, take } from 'rxjs/operators'; // 组件类内添加Subject用于接收搜索值 private searchSubject = new Subject<string>(); ngOnInit(): void { // 初始化防抖逻辑 this.searchSubject.pipe( debounceTime(500), // 500ms内连续输入则忽略前次 takeUntil(this._destroyed$), // 组件销毁时取消订阅 switchMap((searchValue) => { this.searchValue = searchValue; // 先获取当前用户uuid,再触发请求 return this._store.select(selectAuthUserSuccess).pipe(take(1)); }) ).subscribe((response: UserAuthorizations | null) => { const uuid = response?.uuid ?? ''; this._fetchProjectList(uuid); }); } // 重构请求逻辑为单独方法,避免重复订阅 private _fetchProjectList(uuid: string): void { this.loading = true; // 开始加载时设置状态 const reqPayload: ProjectFleetListRequestModel = { limit: this.limit, offset: this.offset, filter: this.searchValue, isFavorite: this.isActive, userUuid: uuid }; this._store.dispatch(Action.loadingProjectFleetListtPayload({ reqPayload })); // 订阅请求结果,用take(1)避免重复订阅内存泄漏 this._store.select(selectProjectFleetListSuccess).pipe( take(1), takeUntil(this._destroyed$) ).subscribe((data) => { if (data?.response) { this.total = data.totalCount ?? 0; this.projectArray = data.response ?? []; this.loading = false; } }); } onValueChange(value: string): void { // 仅向Subject推送新值,防抖逻辑在ngOnInit中处理 this.searchSubject.next(value); } // 组件销毁时完成Subject ngOnDestroy(): void { this._destroyed$.next(); this._destroyed$.complete(); this.searchSubject.complete(); }
方案二:手动维护定时器(适合快速改造)
如果不想引入RxJS Subject,可以手动通过定时器实现防抖:
private debounceTimer: number | null = null; onValueChange(value: string): void { this.searchValue = value; // 清除之前的定时器,确保仅保留最后一次请求 if (this.debounceTimer) { clearTimeout(this.debounceTimer); } // 设置新的500ms定时器 this.debounceTimer = setTimeout(() => { this._initializeProjectList(); }, 500); } // 组件销毁时清除定时器,避免内存泄漏 ngOnDestroy(): void { this._destroyed$.next(); this._destroyed$.complete(); if (this.debounceTimer) { clearTimeout(this.debounceTimer); } } // 优化_initializeProjectList中的uuid异步问题 private _initializeProjectList(): void { this.loading = true; this._store.select(selectAuthUserSuccess).pipe( take(1), takeUntil(this._destroyed$) ).subscribe((response: UserAuthorizations | null) => { const uuid = response?.uuid ?? ''; const reqPayload: ProjectFleetListRequestModel = { limit: this.limit, offset: this.offset, filter: this.searchValue, isFavorite: this.isActive, userUuid: uuid }; this._store.dispatch(Action.loadingProjectFleetListtPayload({ reqPayload })); this._store.select(selectProjectFleetListSuccess).pipe( take(1), takeUntil(this._destroyed$) ).subscribe((data) => { if (data?.response) { this.total = data.totalCount ?? 0; this.projectArray = data.response ?? []; this.loading = false; } }); }); }
关键说明
- 方案一使用RxJS的
debounceTime能更优雅地处理异步流,同时通过switchMap解决了原代码中uuid获取的异步问题。 - 两种方案都在组件销毁时清理订阅/定时器,避免内存泄漏。
- 重构请求方法或优化原初始化逻辑,避免重复订阅Store导致的冗余请求和内存问题。
内容的提问来源于stack exchange,提问作者Noobmaster619
相关产品推荐
相关产品推荐

