# Debounce 概念由來與程式範例 :::info Debounce(防彈跳)是來自電子工程領域的用詞,是一種**避免高頻率、重複性執行事件**的技術,在軟體中主要用於改善效能和減輕系統壓力。 由於在電路學中 Debounce Circuit 常翻譯成「**防彈跳電路**」,因此本文統一稱 debounce 作「防彈跳」。 ::: ## 問題點:Autocomplete 過度頻繁觸發 API 在開發搜尋框功能時,常常會伴隨自動補齊(Autocomplete)功能,即使用者輸入資料時需要查詢資料庫中對應的資料,並顯示在畫面上。類似以下畫面:  然而,如果設計不良,會導致使用者異動資料時大量發送查詢,對前後端造成不必要的計算負擔。因此,我們需要使用 Debounce 的技術,確保只在使用者輸入停頓後才執行最終的搜尋。  ## 什麼是 Debounce 呢?來自電子工程的啟示 Debounce 的概念源於電子工程(Electrical Engineering)領域,用來解決機械開關的訊號雜訊問題,後被廣泛應用於電腦科學(Computer Science)。 當實體機械開關的導體接觸或分離時,金屬的彈性和慣性會造成短暫的 **彈跳(Bouncing)** 現象,導致訊號不穩定。Debounce 的目的,就是在初始彈跳期內忽略所有不穩定的訊號,只接受開關完全穩定後的最終狀態。 >  > 示波器快照顯示,開關在開啟和關閉之間反覆跳動,然後穩定下來。圖片取自[英文維基百科](https://en.wikipedia.org/wiki/Switch#Contact_bounce)。 我們透過兩個生活案例來理解: 1. **遊戲手把** 當玩家只按了一次手把上的「跳躍」按鈕,如果沒有防彈跳機制,遊戲程式可能因為金屬觸點的多次彈跳而接收到多個「按下」訊號。結果遊戲角色可能在瞬間連續跳躍。 2. **電視遙控器** 使用者按了一次「頻道 + 1」,若觸點發生彈跳,遙控器電路會發射多個「換台」指令,電視頻道可能會連跳好幾台。  要解決這個物理問題,有兩種方法: 1. **硬體防彈跳(Hardware Debounce)** 利用電阻-電容(RC) 網路等電子元件。電容的充/放電特性可以將快速變動的雜訊訊號平滑,確保只有穩定的電壓變化能傳遞。 >  > 示意圖,[資料來源](https://www.picotech.com/library/articles/blog/what-is-switch-bounce-how-to-implement-debounce) 2. **軟體防彈跳(Software Debounce)** 利用程式碼解決。一旦偵測到開關狀態改變,程式會啟動一個延遲計時器。在計時器結束前,所有後續的訊號變化都將被忽略或重置。 ## 概念移植到電腦科學 Debounce 這種「避免高頻率、重複性地執行事件」的概念,也被應用到軟體領域: - 後端:在即時監控中,Debounce 可將短時間內重複發生的狀態變更事件延遲並合併,確保資料庫只執行一次最終更新,減輕寫入壓力。 - 前端:搜尋框 Autocomplete 過度頻繁觸發 API、或繪圖程式中使用者拖動滑塊調整參數時,Debounce 能確保複雜的畫面渲染或 API 呼叫只在使用者操作停止後執行一次。 ### 程式概念 軟體 Debounce 必須依賴兩個關鍵步驟: 1. 設置一個**延遲計時器**來執行目標函式 2. 在延遲期間重複呼叫時,必須**取消前一個**未執行的計時器 ```javascript! // 定義 debounce 函式 function debounce(callback, delay) { let timer; return function debouncedCallback(...args) { clearTimeout(timer); timer = setTimeout(() => { callback(...args); }, delay); }; } // 定義要執行的動作 const printSomething = (msg) => console.log(msg); // 建立 debounce 版本的函式 const debouncePrintSomething = debounce(printSomething, 300); // 實際操作時,執行 debounce 版本的函式 debouncePrintSomething("Hello"); // 會被取消 debouncePrintSomething("World"); // 只會在 300 毫秒後印出 "World" ``` ### 核心機制:藉由閉包(Closure)持續追蹤 timer Debounce 的關鍵在於確保所有調用都操作**同一個 `timer` 變數**,這正是透過**閉包**技術實現的: 當 `debounce()` 執行並返回 `debouncedCallback()` 時,`timer` 變數會被這個內部函式持續封存和依賴,形成了閉包,可以有以下好處: - 共享狀態 無論 `debouncePrintSomething()` 被呼叫多少次,所有執行個體都指向並修改同一個被封存的 `timer` 變數。 這使得每一次 `clearTimeout()` 都能讀取到同一個 `timer`,而能精確地取消未完成的任務。 - 隔離保護 由於 `timer` 變數隔離在 `debounce()` 的作用域內,外部程式碼無法直接存取或意外修改它,確保了 `debounce()` 狀態的獨立性。 ## 總結 Debounce 技術的本質是合併短時間頻繁的接觸/呼叫,在前端中,我們透過 `setTimeout` 延遲並以 `clearTimeout` 取消,減少不必要的資源浪費,從而提升整體使用者體驗。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up