Skip to main content

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

· 3 min read

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...