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