Chrome 88 (มกราคม 2021) จะจำกัดตัวจับเวลา JavaScript ที่ลิงก์กันอย่างมากสำหรับหน้าเว็บที่ซ่อนอยู่ในบางเงื่อนไข ซึ่งจะช่วยลดการใช้งาน CPU และการใช้งานแบตเตอรี่ด้วย ในกรณีที่ไม่ปกติบางอย่าง การดำเนินการนี้จะเปลี่ยนลักษณะการทำงาน แต่มักจะใช้ตัวจับเวลาในกรณีที่ API อื่นมีประสิทธิภาพและเชื่อถือได้มากกว่า
โอเค ข้อมูลนี้ค่อนข้างเป็นศัพท์เทคนิคและคลุมเครือไปหน่อย มาเริ่มกันเลย
คำศัพท์
หน้าเว็บที่ซ่อนอยู่
โดยทั่วไป ซ่อนอยู่หมายความว่าแท็บอื่นทำงานอยู่หรือหน้าต่างถูกย่ออยู่ แต่เบราว์เซอร์อาจถือว่าหน้าเว็บซ่อนอยู่เมื่อเนื้อหาของหน้านั้นไม่แสดงเลย เบราว์เซอร์บางประเภทจะดำเนินการเพิ่มเติมจากเบราว์เซอร์อื่นๆ แต่คุณใช้ Page Visibility API เพื่อติดตามได้เสมอเมื่อเบราว์เซอร์คิดว่าระดับการเข้าถึงมีการเปลี่ยนแปลง
ตัวจับเวลา JavaScript
ตัวจับเวลา JavaScript หมายถึง setTimeout
และ setInterval
ซึ่งช่วยให้คุณกำหนดเวลาการเรียกกลับได้ในอนาคต ตัวจับเวลามีประโยชน์และยังคงอยู่ต่อไป แต่บางครั้งก็ใช้เพื่อตรวจสอบสถานะเมื่อเหตุการณ์มีประสิทธิภาพและแม่นยำมากขึ้น
ตัวจับเวลาที่เชื่อมโยงกัน
หากคุณเรียกใช้ setTimeout
ในงานที่เป็นแฮนเดิลการเรียกกลับ setTimeout
การเรียกใช้ครั้งที่ 2 จะ "ต่อท้าย" เมื่อใช้ setInterval
แต่ละรอบจะเป็นส่วนหนึ่งของเชน ตัวอย่างนี้อาจเข้าใจได้ง่ายขึ้นเมื่อดูโค้ด
let chainCount = 0;
setInterval(() => {
chainCount++;
console.log(`This is number ${chainCount} in the chain`);
}, 500);
และ:
let chainCount = 0;
function setTimeoutChain() {
setTimeout(() => {
chainCount++;
console.log(`This is number ${chainCount} in the chain`);
setTimeoutChain();
}, 500);
}
วิธีการทำงานของการจำกัด
การจำกัดจะเกิดขึ้นเป็นระยะๆ ดังนี้
การควบคุมขั้นต่ำ
กรณีนี้จะเกิดขึ้นกับตัวจับเวลาที่ตั้งเวลาไว้เมื่อเงื่อนไขใดเงื่อนไขหนึ่งต่อไปนี้เป็นจริง
- หน้าเว็บแสดงอยู่
- หน้าเว็บส่งเสียงในช่วง 30 วินาทีที่ผ่านมา เสียงนี้อาจมาจาก API การสร้างเสียงใดก็ได้ แต่จะยกเว้นแทร็กเสียงที่ไม่มีเสียง
ตัวจับเวลาจะไม่ถูกจำกัด เว้นแต่ว่าระยะหมดเวลาที่ขอจะน้อยกว่า 4 มิลลิวินาที และจำนวนเชนเป็น 5 รายการขึ้นไป ซึ่งในกรณีนี้ระบบจะตั้งค่าระยะหมดเวลาเป็น 4 มิลลิวินาที การดำเนินการนี้ไม่ใช่สิ่งใหม่ เบราว์เซอร์ต่างๆ ดำเนินการเช่นนี้มาหลายปีแล้ว
การควบคุม
กรณีนี้จะเกิดขึ้นกับตัวจับเวลาที่กําหนดเวลาไว้เมื่อการควบคุมปริมาณขั้นต่ำไม่มีผล และเงื่อนไขใดๆ ต่อไปนี้เป็นจริง
- จํานวนเชนน้อยกว่า 5
- หน้าเว็บซ่อนอยู่ไม่ถึง 5 นาที
- มีการใช้ WebRTC กล่าวโดยละเอียดคือ มี
RTCPeerConnection
ที่มีสถานะ "เปิด"RTCDataChannel
หรือMediaStreamTrack
ที่ "เผยแพร่อยู่"
เบราว์เซอร์จะตรวจสอบตัวจับเวลาในกลุ่มนี้ 1 ครั้งต่อวินาที เนื่องจากมีการตรวจสอบเพียงครั้งเดียวต่อวินาที ตัวจับเวลาที่มีระยะหมดเวลาคล้ายกันจะรวมกลุ่มกันเพื่อรวมเวลาที่แท็บต้องใช้เรียกใช้โค้ด การดำเนินการนี้ไม่ใช่สิ่งใหม่ เบราว์เซอร์ต่างๆ ดำเนินการนี้มาหลายปีแล้ว
การควบคุมอย่างเข้มข้น
โอเค มาดูฟีเจอร์ใหม่ใน Chrome 88 กัน การจำกัดจำนวนสูงสุดจะเกิดขึ้นกับตัวจับเวลาที่กําหนดเวลาไว้เมื่อไม่มีเงื่อนไขการจำกัดจำนวนสูงสุดขั้นต่ำหรือการจำกัดจำนวนใดๆ มีผล และเงื่อนไขทั้งหมดต่อไปนี้เป็นจริง
- หน้าเว็บซ่อนอยู่นานกว่า 5 นาที
- จํานวนเชนเท่ากับ 5 ขึ้นไป
- หน้าเว็บไม่มีเสียงเป็นเวลาอย่างน้อย 30 วินาที
- ไม่ได้ใช้ WebRTC
ในกรณีนี้ เบราว์เซอร์จะตรวจสอบตัวจับเวลาในกลุ่มนี้ 1 ครั้งต่อนาที ซึ่งหมายความว่าตัวจับเวลาจะรวมกันเป็นกลุ่มในการตรวจสอบแบบนาทีต่อนาทีเหล่านี้ เช่นเดียวกับที่เคยเป็นมา
วิธีแก้ปัญหา
โดยทั่วไปแล้วจะมีทางเลือกอื่นที่ดีกว่าตัวจับเวลา หรืออาจใช้ตัวจับเวลาร่วมกับสิ่งอื่นเพื่อถนอมอายุการใช้งานของ CPU และแบตเตอรี่
การสำรวจรัฐ
นี่เป็นการใช้ตัวจับเวลา (ในทางที่ผิด) ที่พบบ่อยที่สุด ซึ่งจะใช้เพื่อตรวจสอบหรือสอบถามอย่างต่อเนื่องเพื่อดูว่ามีการเปลี่ยนแปลงหรือไม่ ในกรณีส่วนใหญ่ จะมีการดำเนินการที่เทียบเท่าการพุช ซึ่งจะแจ้งให้คุณทราบเกี่ยวกับการเปลี่ยนแปลงที่เกิดขึ้น คุณจึงไม่ต้องคอยตรวจสอบ ตรวจสอบว่ามีเหตุการณ์ที่ทําสิ่งเดียวกันหรือไม่
ตัวอย่างมีดังต่อไปนี้
- หากต้องการทราบว่าเมื่อใดที่องค์ประกอบเข้าสู่วิวพอร์ต ให้ใช้
IntersectionObserver
- หากต้องการทราบเมื่อองค์ประกอบเปลี่ยนขนาด ให้ใช้
ResizeObserver
- หากต้องการทราบว่า DOM มีการเปลี่ยนแปลงเมื่อใด ให้ใช้
MutationObserver
หรือการเรียกกลับเกี่ยวกับวงจรชีวิตขององค์ประกอบที่กําหนดเอง - ลองใช้ซ็อกเก็ตเว็บ เหตุการณ์ที่เซิร์ฟเวอร์ส่ง ข้อความ Push หรือสตรีมการดึงข้อมูลแทนการสำรวจเซิร์ฟเวอร์
- หากต้องตอบสนองต่อการเปลี่ยนแปลงระยะของเสียง/วิดีโอ ให้ใช้เหตุการณ์ เช่น
timeupdate
และended
หรือrequestVideoFrameCallback
หากต้องดำเนินการกับแต่ละเฟรม
นอกจากนี้ยังมีทริกเกอร์การแจ้งเตือนหากคุณต้องการแสดงการแจ้งเตือนในเวลาที่เจาะจง
แอนิเมชัน
ภาพเคลื่อนไหวเป็นองค์ประกอบที่แสดงผลภาพ ดังนั้นจึงไม่ควรใช้เวลาของ CPU เมื่อหน้าเว็บซ่อนอยู่
requestAnimationFrame
กำหนดเวลางานภาพเคลื่อนไหวได้ดีกว่าตัวจับเวลา JavaScript มาก โดยจะซิงค์กับอัตราการรีเฟรชของอุปกรณ์ เพื่อให้คุณได้รับเฉพาะการเรียกกลับ 1 ครั้งต่อเฟรมที่แสดงได้ และคุณจะมีเวลาสูงสุดในการสร้างเฟรมนั้น นอกจากนี้ requestAnimationFrame
จะรอให้หน้าเว็บปรากฏขึ้น เพื่อที่จะไม่ใช้ CPU เมื่อหน้าเว็บซ่อนอยู่
หากประกาศภาพเคลื่อนไหวทั้งหมดล่วงหน้าได้ ให้พิจารณาใช้ภาพเคลื่อนไหว CSS หรือ Web Animation API ซึ่งมีข้อดีเช่นเดียวกับ requestAnimationFrame
แต่เบราว์เซอร์จะเพิ่มประสิทธิภาพเพิ่มเติมได้ เช่น การคอมโพสอัตโนมัติ และโดยทั่วไปแล้วใช้งานได้ง่ายกว่า
หากภาพเคลื่อนไหวมีอัตราเฟรมต่ำ (เช่น เคอร์เซอร์กะพริบ) ตัวจับเวลาจะยังคงเป็นตัวเลือกที่ดีที่สุดในตอนนี้ แต่คุณสามารถใช้ร่วมกับ requestAnimationFrame
เพื่อใช้ข้อดีของทั้ง 2 อย่างร่วมกันได้ ดังนี้
function animationInterval(ms, signal, callback) {
const start = document.timeline.currentTime;
function frame(time) {
if (signal.aborted) return;
callback(time);
scheduleFrame(time);
}
function scheduleFrame(time) {
const elapsed = time - start;
const roundedElapsed = Math.round(elapsed / ms) * ms;
const targetNext = start + roundedElapsed + ms;
const delay = targetNext - performance.now();
setTimeout(() => requestAnimationFrame(frame), delay);
}
scheduleFrame(start);
}
การใช้งาน:
const controller = new AbortController();
// Create an animation callback every second:
animationInterval(1000, controller.signal, time => {
console.log('tick!', time);
});
// And stop it:
controller.abort();
การทดสอบ
การเปลี่ยนแปลงนี้จะเปิดใช้สำหรับผู้ใช้ Chrome ทุกคนใน Chrome 88 (มกราคม 2021) ปัจจุบันฟีเจอร์นี้เปิดใช้สำหรับผู้ใช้ Chrome เบต้า เวอร์ชันที่กำลังพัฒนา และ Canary 50% หากต้องการทดสอบ ให้ใช้การติดธงบรรทัดคำสั่งนี้เมื่อเปิด Chrome เวอร์ชันเบต้า กำลังพัฒนา หรือ Canary
--enable-features="IntensiveWakeUpThrottling:grace_period_seconds/10,OptOutZeroTimeoutTimersFromThrottling,AllowAggressiveThrottlingWithWebSocket"
อาร์กิวเมนต์ grace_period_seconds/10
จะทําให้การควบคุมการเรียกใช้อย่างเข้มข้นเริ่มทำงานหลังจากหน้าเว็บซ่อนอยู่ 10 วินาทีแทนที่จะเป็น 5 นาทีเต็ม ซึ่งจะช่วยให้เห็นผลกระทบของการควบคุมการเรียกใช้ได้ง่ายขึ้น
อนาคต
เนื่องจากตัวจับเวลาเป็นแหล่งที่มาของการใช้ CPU มากเกินไป เราจะยังคงมองหาวิธีควบคุมตัวจับเวลาโดยไม่ทำให้เนื้อหาเว็บเสียหาย และ API ที่เราสามารถเพิ่ม/เปลี่ยนแปลงเพื่อให้เป็นไปตาม Use Case เราต้องการยกเลิกการใช้ animationInterval
เพื่อใช้การเรียกกลับภาพเคลื่อนไหวความถี่ต่ำที่มีประสิทธิภาพแทน หากมีข้อสงสัย โปรดติดต่อเราทาง Twitter
รูปภาพส่วนหัวโดย Heather Zabriskie จาก Unsplash