- ใน JavaScript นั้น
setTimeout(0) ไม่ได้ทำงานทันทีจริง ๆ และมักถูกหน่วงอย่างน้อย 4ms ซึ่งเป็นข้อจำกัดพื้นฐานของเบราว์เซอร์เพื่อป้องกันการใช้งานในทางที่ผิด
- ข้อจำกัดนี้มีไว้เพื่อป้องกันไม่ให้เว็บไซต์ใช้ตัวจับเวลาอย่างพร่ำเพรื่อจนทำให้ แบตเตอรี่หมดเร็ว หรือ ประสิทธิภาพการโต้ตอบลดลง และในโหมดประหยัดพลังงานอาจถูกจำกัดหนักขึ้นเป็น 16ms ส่วนแท็บเบื้องหลังอาจเพิ่มเป็น 1 วินาที
- นักพัฒนาได้ใช้ Timer API ทางเลือก หลายแบบเพื่อหลบข้อจำกัดของ
setTimeout เช่น setImmediate, MessageChannel.postMessage, window.postMessage, scheduler.postTask
- จากผลเบนช์มาร์กจริง Chrome และ Firefox ใช้การ clamp ที่ 4ms แต่
MessageChannel และ scheduler.postTask ทำงานได้แทบไม่มีดีเลย์ ขณะที่ Safari มีลักษณะจำกัด setTimeout เข้มงวดกว่า
- โดยแก่นแล้วนี่คือปัญหาเรื่องสมดุลระหว่างการปกป้องประสบการณ์ผู้ใช้กับเสรีภาพของนักพัฒนา ปัจจุบัน Scheduler API กำลังกลายเป็นแนวทางมาตรฐาน แต่หากมีการใช้งานในทางที่ผิด ก็ยังเป็นไปได้ที่จะมี การแทรกแซงของเบราว์เซอร์ (Intervention) แบบใหม่ตามมา
ที่มาของข้อจำกัด setTimeout
การมาของ Timer API แบบอื่น
setImmediate: รองรับเฉพาะใน IE และ Edge รุ่นเก่า ปัจจุบันแทบเลิกใช้แล้ว
MessageChannel.postMessage: ส่งงานเข้าสู่ event loop ผ่านแชนเนลแยก
window.postMessage: ประสิทธิภาพดี แต่มีความเสี่ยงชนกับสคริปต์อื่น
scheduler.postTask: รองรับในเบราว์เซอร์รุ่นใหม่ และถูกมองว่าเป็นตัวเลือกที่เสถียรที่สุด
ผลเบนช์มาร์ก (MacBook Pro 2021, วัดซ้ำ 101 ครั้ง)
- Chrome 139:
setTimeout 4.2ms, scheduler.postTask 0ms
- Firefox 142:
setTimeout 4.72ms, scheduler.postTask 0.01ms
- Safari 18.4:
setTimeout 26.73ms, MessageChannel 0.52ms, window.postMessage 0.05ms
กรณีของ fake-indexeddb
- IndexedDB ต้องการ auto-commit ธุรกรรมทันทีหลังไมโครทาสก์ใน event loop สิ้นสุดลง
setImmediate ของ Node.js เหมาะอย่างยิ่ง แต่ในเบราว์เซอร์ setTimeout ไม่มีประสิทธิภาพ
- งานที่ใช้เวลา 300ms ใน Chrome กลับขยายเป็น 4.8 วินาทีเมื่อรันในเบราว์เซอร์
- วิธีแก้คือใช้
scheduler.postTask เป็นค่าเริ่มต้น และใช้ MessageChannel/window.postMessage เป็น fallback เพื่อความเข้ากันได้
ข้อถกเถียงเรื่องการแทรกแซงของเบราว์เซอร์
- ฝ่ายหนึ่งมองว่าต้องจำกัดตัวจับเวลาเพื่อให้นักพัฒนาป้องกันตัวเองจากการออกแบบที่ไม่เหมาะสม
- อีกฝ่ายมองว่าควร รับประกันเสรีภาพ ให้นักพัฒนาสามารถวัดผลและปรับแต่งได้ด้วยตนเอง
- ท้ายที่สุดตามหลักให้ผู้ใช้มาก่อน เบราว์เซอร์จึงเข้ามาแทรกแซง (intervention) เพื่อป้องกันการใช้งานในทางที่ผิด
- Scheduler API ถูกออกแบบมาเพื่อประนีประนอมสองแนวทางนี้ โดยให้นักพัฒนามี สิทธิ์ควบคุมงาน อย่างละเอียด ขณะเดียวกันก็สอดคล้องกับ rendering pipeline ของเบราว์เซอร์
แนวโน้มต่อจากนี้
postTask และ postMessage น่าจะยังไม่ถูก throttle ไปอีกระยะหนึ่ง
- แต่หากมีการใช้ลำดับความสำคัญสูงอย่าง
user-blocking ในทางที่ผิด ก็อาจเกิดการแทรกแซงอีกครั้งได้
- ในระยะยาว อาจจำเป็นต้องมี API ทางเลือกใหม่อย่าง
scheduler2 อีกก็เป็นได้
ยังไม่มีความคิดเห็น