输入框搜索,显示提示这种需求在应用场景里挺多的。为了减少请求次数,可能有人会用防抖来实现,也有用节流来搞;但是如果没有其它处理,在极端网络环境下可能都会有些问题,本文主要探讨一下这种情况的解决办法,大家有好的想法,也可以放评论区探讨;
防抖与节流
防抖是在一段时间后触发的事件进行执行;
节流是将一串连续的事件在这段时间内分散为每个时间段后执行一次;
个人感觉无论是用防抖还是节流都可解决请求过于频繁的问题,如果想更快得到反馈,可以用节流,如果对接口收费调用的话可以考虑防抖。
但是如果网络有波动的情况下,可能都有一定的问题,看下请求流程可以清楚的看到问题:
如果某次接口请求特别慢,会覆盖最新的响应,导致显示不匹配的查询结果;
解决方案
- 可以在请求中和响应中放置冗余参数,例如时间戳,在响应回调中判断时间是否早于最新时间戳来判断是否应用该响应;
const [value, setValue] = useState('');
const debouncedValue = useDebounce(value,{wait:300});
const last = useRef(Date.now());
const getData = useCallback(async (value) => {
last.current = Date.now();
const data = await fetchPromt({time: last.current, value});
if (data.time < last.current) {
return;
}
update();
}, []);
useEffect(() => {
getData(value);
}, [debouncedValue, getData]);
- 递归调用
const [value, setValue] = useState('');
const debouncedValue = useDebounce(value,{wait:300});
const last = useRef(Date.now());
const lock = useRef(false);
const collect = useRef([]);
const getData = useCallback(async () => {
if (lock.current || !collect.ref.length) {
return;
}
try {
lock.current = true;
const value = collect.current.pop();
collect.current.length = 0;
const data = await fetchPromt({value});
update();
} catch (e) {
} finally {
lock.current=false;
if (collect.ref.length) {
getData();
}
}
}, []);
useEffect(() => {
collect.current.push(debounceValue);
getData();
}, [debouncedValue]);
第一次防抖事件产生后,经过一段时间在发送一个防抖事件
分两种情况:
- 请求在第二次前完成,则不触发finally里的的getData, 此时collect收集器为空;
- 请求在第二次后未完成,则在完成后会触发finally里的getData,从而保持响应顺序;