防抖和竞态问题
防抖很重要,尤其是在前后端有网络IO的情况下,如果用户每次操作都要往后端发一个请求,那流量费和后端服务器压力就会很大。标准处置方法就是加个延迟,用户在这个时间段内的连续操作,只取最后一次(或者是最终结果)。 防抖这方面,Angular的RxJS库里面有搭配的方法:
// 其他引入省略
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
@Component({ ... })
export class SearchComponent {
// 1. 创建一个表单控制器,它本身就是一个 Observable 源
searchControl = new FormControl('');
// 2. 定义结果流
// 如果如果 Observable 是水管,这里的.pipe就可以理解成一个过滤器
// 进去之后进行一堆加工,最后的输出处理之后的结果
// valueChanges 负责监听值变化事件,若有则开始加工
users$ = this.searchControl.valueChanges.pipe(
// --- 管道开始 ---
// 注意下面三个是任选的,其实功能有重合,不必要全加进去
// 防抖:300ms内如果用户还在打字,就不处理
debounceTime(300),
// 去重:如果用户输入 "A" -> 删掉变成 "" -> 又输入 "A",
// 值没变,就不发请求
distinctUntilChanged(),
// SwitchMap
// 处理竞态问题,一旦有新值过来,立刻取消上一次还没完成的 HTTP 请求
// 永远只保留最后一次请求的结果
switchMap(term => {
// 如果为空,返回空数组,不发请求
if (!term) return [];
// 发起新的请求
return this.http.get<User[]>(`/api/search?q=${term}`);
})
);
constructor(private http: HttpClient) {}
}
竞态问题,具体来说就是因为网络波动,导致用户发送的结果加载时序错乱,导致当下请求被后返回的先前数据覆盖的情况发生。switchMap 就是用来解决这个问题,如果数值发生变动,则上一次的请求就会直接取消,永远只取最后一次的结果。