Throttling คืออะไร แตกต่างกับ Debouncing อย่างไร (ตอนจบ)
Table of contents
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 ที่กำหนดไว้
// เพิ่มกำหนดตัวแปร Timeout ขึ้นมาตัวนึง
const timeout = 1000
let isWaiting = false
inputField.addEventListener('input', () => {
if (!isWaiting) {
isWaiting = true
callback()
setTimeout(() => (isWaiting = false), timeout)
}
})
การนำ Throttling ไปใช้
- ใช้ Throttling เพื่อหน่วงการทำงานหลังการ Click เพื่อป้องกันไม่ให้เกิด Spam click
- ใช้ Throttling เพื่อหน่วงเวลาเมื่อเรียก API
- ใช้ Throttling กับ event handler mousemove/touchmove
- ใช้ Throttling กับการสร้างเกมส์ เช่น เงื่อนไขการยิง เงื่อนไขการชก ที่ใช้หน่วงเวลาป้องกันการกดปุ่มรัว ๆ
- ใช้ 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)
() => { 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...' /> </> ) }
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 เป็นเทคนิคที่เรียบง่ายและเข้าใจง่าย และยังช่วยปรับปรุงประสิทธิภาพอย่างมาก หวังว่าผู้อ่านจะสามารถปฏิบัติและนำแนวคิดนี้ไปใช้ได้อย่างมีประสิทธิภาพ ขอบคุณที่อ่านจนจบ 😊