Skip to main content

Throttling คืออะไร แตกต่างกับ Debouncing อย่างไร (ตอนจบ)

Kongvut Sangkla

Intro

สวัสดีครับ จากบทความครั้งก่อนได้พูดถึง เทคนิค Debouncing โดยบทความนี้จะพูดถึงเทคนิค Throttling ที่มีความคล้ายคลึงกัน ซึ่งเป็นเทคนิคที่มีประโยชน์ช่วยเพิ่มประสิทธิภาพ Performance ได้ทั้ง Frontend App และ Backend App ที่ช่วยแก้ปัญหาการเกิด callback จำนวนมากจาก Event listeners มีรายละเอียดดังนี้

Throttling

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

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

จากตัวอย่างด้านล่างเป็นการใช้ Timeout เพื่อกำหนด Delay 1000 ms และมีตัวแปร isWaiting เพื่อตรวจสอบการรอคอย

จากนั้นเมื่อเกิด Events ใด ๆ ก็จะถูก Ignore และหน่วงการทำงาน (ไม่ให้ทำงานทันที)

เมื่อสิ้นสุด Delay 1000 ms และ isWaiting เป็น false โปรแกรมก็จะทำงานตาม Callback ที่กำหนดไว้

แนวคิดของ Throttling
// เพิ่มกำหนดตัวแปร Timeout ขึ้นมาตัวนึง
const timeout = 1000
let isWaiting = false

inputField.addEventListener('input', () => {
if (!isWaiting) {
isWaiting = true
callback()
setTimeout(() => (isWaiting = false), timeout)
}
})

การนำ Throttling ไปใช้

  1. ใช้ Throttling เพื่อหน่วงการทำงานหลังการ Click เพื่อป้องกันไม่ให้เกิด Spam click
  2. ใช้ Throttling เพื่อหน่วงเวลาเมื่อเรียก API
  3. ใช้ Throttling กับ event handler mousemove/touchmove
  4. ใช้ Throttling กับการสร้างเกมส์ เช่น เงื่อนไขการยิง เงื่อนไขการชก ที่ใช้หน่วงเวลาป้องกันการกดปุ่มรัว ๆ
  5. ใช้ Throttling เพื่อป้องกันการเกิด Requests จำนวนมากในการ Load ข้อมูลใหม่เมื่อ Scroll event หน้าเว็บเพจ

ตัวอย่างการใช้งาน Throttling

แบบไม่ได้ใช้เทคนิค Throttling

เมื่อใช้เทคนิค Throttling

ความแตกต่างระหว่าง Throttling Vs. Debouncing

Throttling และ Debouncing มีหลักการทำงานคล้ายคลึงกันโดยอาศัย Delay แต่มีความแตกต่างกันดังนี้

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

  • Regular คือวิธีแบบปกติที่ไม่ใช้เทคนิคใด ๆ โดยที่ทุก ๆ Input จะเกิด Execute
  • Debounce คือวิธีแบบที่ใช้เทคนิค Debouncing โดยที่ทุก ๆ Input จะไม่ทำงานทันทีจนกว่า จะเลิกทำกระทำกับ Input แล้วครบ Delay (2000 ms)
  • Throttle คือวิธีแบบที่ใช้เทคนิค Throttling ซึ่งจะทำงานครั้งแรกเมื่อมี Input และถ้ายังมี Input อื่น ๆ ก็ จะไม่ทำงานทันทีจนว่าจะครบ Delay (2000 ms) ที่ยังมีการกระทำกับ Input

Throttling with Lodash

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

import _ from "lodash"

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

const throttleLogHi = _.throttle(logHi, 100)

throttleLogHi()

// output: Hi

Throttling with React

Basically

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

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

  function throttle(callback, delay) {
    let isWaiting = false
    return (...args) => {
      if (!isWaiting) {
        isWaiting = true
        callback(...args)
        setTimeout(() => (isWaiting = false), delay)
      }
    }
  }

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

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

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

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

by Lodash

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

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

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

import _ from "lodash"

const { throttle } = _

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

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

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

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

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

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

สรุป

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

References

Loading...