了解 debounce & throttle 來處理事件效能的問題。
前言
在 Youtube 直播看到 Alex 大大 線上分享 JS 的運用,這次分享處理事件效能的問題,
在此紀錄相關資訊,方便以後複習。
連續畫面的渲染 requestAnimationFrame
如果我們有利用 setTimeout
或 setInterval
要處理連續渲染畫面的需求的話,
建議可以改使用 requestAnimationFrame
,
避免有時會因為 setTimeout
或 setInterval
設定的時間不正確或太密集的話,
導致畫面 LAG 或不正常的情況發生。
事件效能問題
當 JS 遇到頻繁觸發事件(如:監聽 window 的 scroll、resize 事件、user 瘋狂 Click 按鈕…等) 的情況,
有時可能會造成效能問題,而且常理來說也不太可能會在幾毫秒間,就需要那麼頻繁觸發 Event,
所以我們就可以利用 延遲執行 這種方法,來減緩觸發 Event 的次數,
下列分別介紹 debounce
與 throttle
二種差異與用法。
debounce
因為 debounce
有很多版本的寫法,所以附上我說明的版本。
1 | function debounce(func, wait = 20, immediate = true) { |
情境描述:
debounce(function, wait, [immediate])
- function = 給糖果
- wait: 需要冷靜的時間
- immediate: 先給或後給
假設我設定 wait:10 秒 以及 immediate:先給 的情況下,
有一個小孩第一次跟媽媽吵著要糖吃(觸發事件,如 Scroll、resize…),
而 debounce 媽媽先給小孩糖果(完成任務),
接著小孩立馬即時瞬間不間斷的,又要瘋狂吵鬧要吃糖(瘋狂觸發事件),
媽媽說你要有達到冷靜 10 秒後,才會再給糖果,否則就不給糖。
這時有二種情況
- 小孩依舊瘋狂吵要吃糖,沒有冷靜超過 10 秒以上,這時媽媽怎樣都不會給糖。
- 小孩吵了一陣子,終於 有冷靜 10 秒以上,這時媽媽才再給他一顆糖。
說了那麼多,總結來說就是
當觸發事件完成任務後,下次要再可以成功觸發完成任務的條件,
就是需停止觸發事件且超過 wait 設定的時間,才可以再觸發完成一次任務。
throttle
附上解說的版本,若 limit = 0
的話,就等於 debounce 了。
1 | function throttle(fun, { wait = 33, limit = 0, immediate = false }) { |
情境描述:
一樣是「會吵的孩子有糖吃」的故事。
throttle(fun, { wait = 33, limit = 0, immediate = false })
- fun = 給糖果
- wait: 需要冷靜的時間
- limit: 超過最小間隔時間,就給糖
- immediate: 先給或後給
假設我設定 wait:10 秒 、 limit:5 秒 以及 immediate:先給 的情況下,
小孩第一次要糖吃時,throttle 媽媽一樣先給小孩糖果,
但天下小孩一樣白目(有白色的眼球),立馬又吵要糖吃,
小孩吵鬧依舊沒停止且超過 10 秒,不過 throttle 媽媽就比較心軟,
小孩雖然一直吵,但媽媽只要每超過 5 秒一次就會心軟給小孩一個糖吃。
最後總結來說就是
當觸發事件完成任務後,有二種情況可以再觸發完成一次任務
- 停止觸發事件 且 超過wait設定的時間。
- 連續觸發時,中間間隔時間有超過 limit設定的時間。
閉包(Closure)
上述 debounce
與 throttle
這二段程式碼中,都有使用到 閉包(Closure)的用法了。
以 debounce
為例,他利用 timeout
這變數來紀錄且進行一些判斷,下列為他的生命週期
- 判斷是否第一次執行,再看
immediate
變數,是設定 先執行 or 後執行。 - 若是連續觸發時,就清除上一次的
setTimeout
。 - 當連續觸發的最後一次時,就會執行
later函式
,並清空timeout
變數。
這樣下次連續觸發時,就會再回到 1.
開始重新判斷。
1 | function debounce(func, wait = 20, immediate = true) { |
1. 當宣告addEventListener時,會執一次
debounce函式
,並回傳新的函式,此時就完成閉包(Closure)的寫法。2. 之後每當觸發事件時,都是執行
新的函式
,就是上例的第 6 ~ 27 之間,而 timeout
就有點像是新函式的全域變數,但不是真的在window的全域變數。(jsbin-捲動畫面並開console看)3. JS是由上往下逐行執行,記得看到最後一行才算結束。
4.
later函式
一開始只是宣告,沒執行,最後要等setTimeout
才會執行