防抖(debounce)和節流(throttle)---解決事件頻繁觸發形成頁面卡死

防抖(debounce)和節流(throttle)

連續觸發(觸發頻率很高)的時間,不進行優化,會出現頁面卡頓現象。
常見的須要優化的事件:html

  • 鼠標事件:函數

    • mousemove(拖拽)
    • mouseover(劃過)
    • mouseWheel(滾屏)
  • 鍵盤事件:post

    • keydown(按下鍵盤)
    • keypress(按下字符鍵盤)
    • keyup(彈起鍵盤)
  • window resize/scroll優化

    • DOM 元素動態定位

優化方式是控制事件處理器在一段時間內的執行次動畫

防抖

頻繁(連續)觸發事件(好比用戶觸發輸入事件input),不執行目標動做,當不在觸發事件了,再執行。code

實現思路,在事件處理器內,使用 setTimeout 包裹目標動做,一直觸發事件,就清除上次的定時器,再也不觸發觸發事件,會執行最後一個定時器,目標動做也執行一次了。htm

<input type="text" id="input" />

JS代碼:seo

function debounce(callback, delay) {
  let timeout = 0;
  return e => {
    console.log('清除', timeout, new Date());
    clearTimeout(timeout); //input 一直觸發,就清除上一次的定時器,防止執行目標函數,直到事件不觸發事件,最後一個定時器沒有清除,delay 時間後就會執定時器,就確保了目標函數只執行一次。
    timeout = setTimeout(() => {
      callback(e);
    }, delay);
    console.log('新的', timeout, e.target.value, new Date());
  };
}
let print = debounce(e => {
  let value = e.target.value;
  console.log(value, new Date());
}, 1000);
document
  .querySelector('#input')
  .addEventListener('input', print, false);

清除定時器的時機很關鍵,在新定時器生成以前,若是在以後,會將全部定時器都清除,目標函數一次都不執行。事件

節流

防抖是屢次觸發事件,目標函數只執行一次,無論觸發這些事件用了多少時間。而節流是在一段時間內,確保目標函數只執行一次,實現緩慢執行目標函數的效果。get

上面的輸入使用節流實現:

let thorttle = (callback, delay) => {
  let timeout = 0;
  let now = new Date() - 0;
  return e => {
    console.log('now', now);
    let last = new Date() - 0;
    clearTimeout(timeout);
    if (last - now >= delay) {
      console.log('時間間隔', last - now);
      callback(e);
      now = last;//將上執行的時間賦值給 now
    } else {
      //將 delay 時間內屢次觸發事件,目標函數合併到這裏執行
      timeout = setTimeout(() => {
        callback(e);
      }, delay);
    }
  };
};
let write = thorttle(e => {
  console.log(e.target.value, new Date());
}, 5000);
document
  .querySelector('#input')
  .addEventListener('input', write, false);

二者比較

節流在某個時間段內,目標函數能執行一次,限制目標函數的執行頻率,無論事件觸發了多少次;
防抖是屢次觸發事件,目標函數只執行一次,無論觸發了這些事件用了多少時間。

節流函數限制目標函數的執行頻率,有連續變化的效果,適用於關注變化過程的操做,能夠調整目標函數執行頻率使得變化更加平滑,好比動畫、改變窗口時執行某些操做等,經常使用事件resizescrollmouseWheeltouchmovemouseover等;

防抖函數適用於更關注結果的操做,不太關注操做過程,常見的事件有 inputkeyup等。

最後看一個 將 防抖 和 節流都用 resize 事件的效果,更能體會二者的區別:

function debounce(callback, delay) {
  let timeout = 0;
  return e => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      callback(e);
    }, delay);
  };
}
let print = debounce(e => {
  let value = e.target.value;
  console.log('debounce', window.innerWidth);
}, 500);
let thorttle = (callback, delay) => {
  let timeout = 0;
  let now = new Date() - 0;
  return e => {
    let last = new Date() - 0;
    clearTimeout(timeout);
    if (last - now >= delay) {
      callback(e);
      now = last;
    } else {
      timeout = setTimeout(() => {
        callback(e);
      }, delay);
    }
  };
};
let write = thorttle(e => {
  console.log('thorttle', window.innerWidth);
}, 500);
window.addEventListener('resize', write, false);
window.addEventListener('resize', print, false);

參考

函數節流與函數防抖
函數防抖與函數節流

相關文章
相關標籤/搜索