Skip to main content

Debouncing คืออะไร ?

Kongvut Sangkla

Intro

สวัสดีครับ บทความนี้จะพามารู้จักกับ Debouncing โดย Debouncing เป็นเทคนิคที่มีประโยชน์ในการเพิ่มประสิทธิภาพ Performance ของ Client-side applications โดยเป็นหนึ่งเทคนิคในหลายสิ่งที่สำคัญในการพัฒนา Web App และ Software ซึ่งตามปกติโปรแกรมเมอร์จำเป็นต้องเขียนโค้ดโปรแกรมโดยคำนึง Performance เพื่อที่จะเพิ่มประสบการณ์ผู้ใช้งานที่ดี (UX) กับ Software

พื้นฐานเรื่อง Event Listeners

ก่อนที่จะไปดูว่า Debouncing คืออะไรมาทำความเข้าใจเกี่ยวกับ Event listeners ก่อน

Syntax
element.addEventListener(event, callbackFunction);

ในการพัฒนา Client-side Applications นั้นจะมีเรื่อง Event listeners คือสิ่งที่ขาดไม่ได้สำหรับการ Interacts กับผู้ใช้งานโดยตัวอย่างของ Events เช่น

  • การคลิกปุ่ม
  • การ Scrolling หน้า
  • การกรอกข้อความกับช่อง Input field
  • submit ฟอร์ม และอื่น ๆ

โดย Event listeners จะมี Callback function เพื่อใช้ Triggers เมื่อเกิด Events ต่าง ๆ

จากตัวอย่างด้านล่างนี้จะเห็นว่าทุก ๆ ครั้งที่มี Events ที่ Input จะเกิด Requests (API) = จำนวนของ Events

ปัญหา Callbacks จำนวนมากจาก Event listeners

ในบางโอกาส Event listeners เหล่านี้มักจะเกิดขึ้นจำนวนมากทำให้มีปัญหาเรื่องประสิทธิภาพเมื่อถูกเรียก Callbacks ดังนั้นควรมีการควบคุม Callbacks เหล่านี้เท่าที่จำเป็นจึงเป็นที่มาของเทคนิค Debouncing

แก้ปัญหาด้วย Debouncing

แนวคิด Debouncing

Debouncing คือการหน่วงเวลา (Delay) ในระยะหนึ่งก่อนที่จะให้ทำงานใด ๆ โดยมีเงื่อนไขดังนี้

  • จะทำงานเมื่อหมดเวลาของ Delay และ User ไม่มีการกระทำใด ๆ กับ Input interface และเกิด Event
  • แต่เมื่อ User มีการกระทำใด ๆ กับ Input ก่อนหมดเวลา Delay ค่าของ Delay จะเริ่มต้นใหม่เสมอ
  • ค่าของ Delay จะเริ่มต้นใหม่ไปเรื่อย ๆ จนกว่า User ไม่มีการกระทำใด ๆ กับ Input

จากตัวอย่างด้านบนเป็นการใช้ Timeout เพื่อกำหนด Delay 500 ms เมื่อเกิด Events ใด ๆ เพื่อให้หน่วงการทำงาน (ไม่ให้ทำงานทันที)

เพราะว่าอาจจะเกิด Events อื่น ๆ ก่อนที่จะให้ทำงานจริง ๆ (เช่น การพิมพ์ตัวอักษรหลาย ๆ ตัวที่พิมพ์ยังไม่เสร็จ) ทำให้การ httpRequest() จะยังไม่ทำงานทันที

จากนั้นเมื่อสิ้นสุด Delay 500 ms และไม่เกิด Events ใด ๆ อีกโปรแกรมก็จะทำงานตาม Callback ที่กำหนดไว้คือ httpRequest()

ค่า Delay xxx ms สามารถกำหนดตัวเลขได้ตามที่ต้องการ ขึ้นอยู่กับบริบทว่าต้องการ Delay เวลานานเท่าไหร่ -> เพื่อจะไม่ input ใด ๆ อีก

อธิภายการทำงานจากรูปภาพ

  • Regular คือวิธีแบบปกติที่ไม่ใช้เทคนิคใด ๆ โดยที่ทุก ๆ Input จะเกิด Execute
  • Debounce คือวิธีแบบที่ใช้เทคนิค Debouncing โดยที่ทุก ๆ Input จะไม่ทำงานทันทีจนกว่า จะเลิกทำกระทำกับ Input แล้วครบ Delay (1500 ms)
แนวคิดของ Debouncing
// เพิ่มกำหนดตัวแปร Timeout ขึ้นมาตัวนึง
let timeout;

inputField.addEventListener('input', () => {
// เมื่อเกิด Events ของ Input ให้ Reset ค่าใน Timeout
clearTimeout(timeout)
// เมื่อสิ้นสุด Delay 500 ms จะทำงานตาม Callback ที่กำหนดไว้คือ httpRequest()
timeout = setTimeout(httpRequest, 500)
})

Use cases การนำไปใช้

  1. ใช้ Debouncing กับการ Resize รูปภาพ
  2. ใช้ Debouncing กับ Event ที่มีคุณสมบัติ Autosave
  3. เพื่อไม่ต้องการให้เกิดการทำงานอื่น ๆ ในขณะเกิด Drags and drops event
  4. เพื่อไม่ให้มี Requests (Axios) ใด ๆ จนกระทั้งผู้ใช้งานหยุดกระทำกับ Input

Debouncing with Helper functions

ส่วนนี้คือเทคนิคที่แถมเพิ่มโดยแทนที่จะเขียน Debouncing แบบ Manual ทุกรอบเราสามารถสร้าง Helper functions ที่ช่วยให้ง่ายและสะดวกในการเรียกใช้งานบ่อย ๆ ดังนี้

// helper.js
export function debounce(callback, delay) {
let timeout

return function () {
clearTimeout(timeout)
timeout = setTimeout(callback, delay)
};
}

// ตัวอย่างการใช้งาน
import { debounce } from '/helper.js'

debounce(httpRequest(), 600)

Debouncing with Lodash

ในภาษา JS เราไม่จำเป็นต้อง Implement Debouncing เองให้เสียเวลา โดยเราสามารถใช้ Library ของ Lodash ช่วยดังนี้

import _ from "lodash"

const logHi = () => console.log('Hi')

const debouncedLogHi = _.debounce(logHi, 1500)

debouncedLogHi()
debouncedLogHi()
debouncedLogHi()

// output: Hi
import _ from "lodash"

const logMessage = message => console.log(message)

const debouncedLogMessage = _.debounce(logMessage, 1500)

debouncedLogMessage('first message')
debouncedLogMessage('second message')
debouncedLogMessage('third message')

// output: third message

Debouncing with React

Basically

ตัวอย่างการใช้ Debouncing กับ React โดยพื้นฐาน (สร้างเองแบบไม่ใช้ Libs)

Live Editor
() => {
  const [requestsCount, setRequestsCount] = useState(0)
  const [input, setInput] = useState([])

  function debounce(callback, delay) {
    let timeout
    return (...args) => {
      clearTimeout(timeout)
      timeout = setTimeout(() => {
        callback(...args)
      }, delay)
    }
  }

  const request = (e) => {
    // request statement

    // display text input
    setInput(prev => [...prev, e.target.value])

    // count requests
    setRequestsCount(prev => ++prev)
  }

  const debouncedInputHandler = useMemo(() => debounce(request, 1000), [])
  
  return (
    <>
      <p>Input: {JSON.stringify(input, null, 2)}</p>
      <h3>แก้ปัญหาด้วย Debouncing</h3>
      <h3>จำนวนการเกิด Requests: {requestsCount}</h3>
      <input type='text' onInput={debouncedInputHandler} placeholder='Enter text...' />
    </>
  )
}
Result
Loading...

by Lodash

นี่คือตัวอย่างการใช้ Debouncing ใน React และใช้ Library จาก Lodash

แต่จะมีการใช้ useMemo และ useCallback เข้ามาช่วยป้องกันปัญหาเรื่อง Re-renders ที่ส่งผลต่อประสิทธิภาพ

อ่านเนื้อหาเพิ่มเติมเกี่ยวกับการใช้งาน useMemo และ useCallback อย่างละเอียดได้ที่นี่ "การใช้งานและความแตกต่างระหว่าง useMemo และ useCallback ของ React Hooks"

import _ from "lodash"

const { debounce } = _

() => {
// Request statement
function httpRequest(input) {
// Call API ...
}

// ใช้แบบนี้ก็ได้ แต่จะมีปัญหาเรื่อง Re-renders
//const debouncedInputHandler = debounce(inputHandler, 500)

// แนะนำให้ใช้ useCallback เข้ามาช่วยลดปัญหา Re-renders และให้ประสิทธิภาพที่ดีกว่า
const debouncedInputHandler = useCallback(
debounce(inputHandler, 500), []
)

// หรือจะใช้ useMemo ก็ได้เช่นกัน
/*const debouncedInputHandler = useMemo(() => {
return debounce(changeHandler, 500);
}, []);*/

const inputHandler = (event) => {
const input = event.target.value
httpRequest(input)
}

return (
<>
<input type="text" onInput={debouncedInputHandler}></input>
</>
)
}

สรุป

info

แนวคิดเทคนิค Debouncing นั้นไม่ได้จำกัดใช้กับเฉพาะ Frontend App ใน Backend App ก็สามารถใช้เทคนิค Debouncing สำหรับประยุกต์การทำ Delay สำหรับ Processing ได้เหมือนกัน

Debouncing เป็นเทคนิคที่เรียบง่ายและเข้าใจง่าย และยังช่วยปรับปรุงประสิทธิภาพอย่างมาก หวังว่าผู้อ่านจะสามารถปฏิบัติและนำแนวคิดนี้ไปใช้ได้ โดยบทความต่อไปจะพูดถึงอีกเทคนิคที่คล้าย ๆ กันคือ Throttling

*อัปเดทเนื้อหา

References

Loading...