防抖:觸發事件後在n秒內函數只執行一次,若在n秒內再次觸發則重新計算下次函數響應時間
節流:連續發生的事件在n秒內只執行一次
使用場景:即時查詢等需要短時間內請求到服務器端的數據,以一定時間間隔調用函數的場景
代碼展示:
下面以鼠標的onmousemove
事件來展示防抖的非立即執行和立即執行版,節流的定時器和時間戳版。
準備代碼:
<div id="content"
style="height: 150px;line-height: 150px;text-align: center;background-color: aquamarine;font-size: 30px;color: brown;">
</div>
<script>
let num = 1;
let content = document.getElementById('content');
function count() {
content.innerHTML = num++;
}
</script>
此時界面上有一個盒子,在盒子內可以滑動鼠標。
無防抖和節流版:鼠標一動,界面上展示的數字就加1,一動就加1
// 鼠標一經過就執行函數
content.onmousemove = count
防抖的非立即執行版:讓鼠標移動完畢後過1秒在查詢
function debounce(func, wait){
let timeout; // 定時器
return function() {
if(timeout) clearTimeout(timeout);
timeout = setTimeout(function(){
func.apply(this); // 執行func函數
}, wait)
}
}
content.onmousemove = debounce(count, 1000)
防抖的立即執行版:讓鼠標移動完畢立即查詢,過1秒才能在查詢
function debounce(func, wait){
let timeout;
return function(){
if(timeout) clearTimeout(timeout); // 取消之前的任務
let callNow = !timeout; // 類型轉化
timeout = setTimeout(() => { // 增加一個定時器
timeout = null // 清空當前定時器句柄
}, wait)
if(callNow) func.apply(this) // 第一次執行
}
}
content.onmousemove = debounce(count, 1000)
節流的定時器版:啓用定時器,固定的時間去發請求
function throttle(func, wait){
let timeout; // 定義一個定時器句柄
return function(){
if(!timeout){ // 是否存在定時器
timeout = setTimeout(() => { // 創建一個定時器
timeout = null;
func.apply(this) // apply調用函數func即count
}, wait)
}
}
}
content.onmousemove = throttle(count, 1000)
節流的時間戳版:當前時間減去上次請求的時間,固定的時間去發請求
function throttle(func, wait) {
let prev = 0; // 上次記錄的事件
return function () {
let now = Date.now() // 當前時間
if (now - prev > wait) { // 當前時間 - 上次時間 > 等待時間
func.apply(this) // 執行函數 發送請求
prev = now // 重置上次記錄時間
}
}
}
content.onmousemove = throttle(count, 1000)
總結:通過防抖和節流,在持續觸發事件的過程中可以很好的控制與後臺服務交互的次數,減少服務器端的壓力。