original post2026-08-12
Haru Li
Frontend Developer
14 posts
·2 min read效能JavaScript
防抖與節流:同一串輸入,送出次數差多少
debounce 和 throttle 是那種「知道有這兩個東西,但每次都要想一下差在哪」的名詞。我做這個 demo 就是為了不用再想。
發想:把次數印出來
輸入框每打一個字,就算一次「要送出的請求」。三種策略同時處理同一串輸入,各自計數。
不用解釋定義,看數字就懂。
測試:快速連打一段字再停手
三個計數器:
- 原始:每次輸入都送。次數會跟你的按鍵數一模一樣。
- 防抖(debounce):打字期間一次都不送,停手之後才送一次。
- 節流(throttle):打字期間穩定地送,但有固定的間隔上限。
打一段長一點的字最明顯:原始可能二十幾次,防抖只有 1 次,節流落在中間且跟你打字的時間長度成正比,跟按了幾個鍵無關。
理解:兩種完全不同的問題
防抖問的是「你講完了嗎」。每次有新事件就把計時器重設,只有安靜超過設定的時間才真的執行。
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer); // ← 重點:每次都重設
timer = setTimeout(() => fn(...args), ms);
};
}
節流問的是「上次是多久以前」。在設定的間隔內最多執行一次,中間的事件直接丟掉。
function throttle(fn, ms) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last < ms) return; // ← 重點:還沒到時間就不做
last = now;
fn(...args);
};
}
差別在中間那段時間有沒有反應。防抖是完全安靜,節流是穩定輸出。
講解:怎麼選
| 情境 | 該用 | 為什麼 |
|---|---|---|
| 搜尋建議 | 防抖 | 打到一半的字送出去沒有意義 |
| 表單即時驗證 | 防抖 | 同上,打到一半就報錯很煩 |
| 捲動位置追蹤 | 節流 | 捲動期間就需要持續更新 |
| 拖曳 / 視窗縮放 | 節流 | 過程中畫面要跟著動 |
| 送出按鈕防連點 | 節流 | 第一次要立刻生效,後續擋掉 |
一個判準:「過程中需不需要反應」。需要就節流,不需要就防抖。
還有一個常被忽略的細節:防抖的延遲要選得夠短,不然使用者會覺得系統遲鈍。搜尋建議常用 200–300 ms,長到 500 ms 以上就會有「怎麼還沒出來」的感覺。
(另外,捲動監聽現在多數情況可以直接用 IntersectionObserver 取代,連節流都不必——這一頁的錨點導覽就是這樣做的。)
這個 demo 可以在 效能實驗室 自己操作。
