เว็บไซต์และแอปจํานวนมากใช้สคริปต์จํานวนมาก บ่อยครั้งที่ JavaScript ต้องทำงานโดยเร็วที่สุด แต่ในขณะเดียวกันคุณก็ไม่ต้องการให้ JavaScript ขัดจังหวะผู้ใช้ หากคุณส่งข้อมูลวิเคราะห์เมื่อผู้ใช้เลื่อนหน้าเว็บ หรือเพิ่มองค์ประกอบต่อท้าย DOM ขณะที่ผู้ใช้แตะปุ่มอยู่ เว็บแอปอาจไม่ตอบสนอง ซึ่งส่งผลให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ไม่ดี
ข่าวดีคือตอนนี้เรามี API ที่จะช่วยคุณในเรื่องต่อไปนี้ requestIdleCallback ในทำนองเดียวกับที่การใช้ requestAnimationFrame ช่วยให้เรากำหนดเวลาภาพเคลื่อนไหวได้อย่างเหมาะสมและเพิ่มโอกาสในการเล่นที่ 60 fps ให้ได้สูงสุด requestIdleCallback จะกำหนดเวลาทำงานเมื่อมีเวลาว่างเมื่อสิ้นสุดเฟรมหรือเมื่อผู้ใช้ไม่ได้ใช้งาน ซึ่งหมายความว่าคุณมีโอกาสที่จะทํางานโดยไม่รบกวนผู้ใช้ ฟีเจอร์นี้พร้อมใช้งานใน Chrome เวอร์ชัน 47 แล้ว คุณจึงลองใช้ได้เลยวันนี้โดยใช้ Chrome Canary ฟีเจอร์นี้เป็นฟีเจอร์ทดลองและข้อมูลจำเพาะยังอยู่ระหว่างการเปลี่ยนแปลง จึงอาจมีการเปลี่ยนแปลงในอนาคต
เหตุใดฉันจึงควรใช้ requestIdleCallback
การกำหนดเวลางานที่ไม่จำเป็นด้วยตนเองนั้นทำได้ยากมาก คุณจะไม่สามารถทราบเวลาเฟรมที่เหลืออยู่ได้อย่างแน่นอน เนื่องจากหลังจากการเรียก requestAnimationFrame กลับจะต้องมีการคํานวณสไตล์ เลย์เอาต์ การวาด และข้อมูลภายในอื่นๆ ของเบราว์เซอร์ที่จำเป็นต้องทำงาน โซลูชันที่ติดตั้งใช้งานเองไม่สามารถรองรับสิ่งเหล่านี้ หากต้องการแน่ใจว่าผู้ใช้ไม่ได้โต้ตอบด้วยวิธีใดวิธีหนึ่ง คุณจะต้องแนบ Listener กับเหตุการณ์การโต้ตอบทุกประเภท (scroll, touch, click) ด้วย แม้ว่าจะไม่ต้องใช้ Listener เหล่านั้นเพื่อฟังก์ชันการทำงาน เพียงเพื่อให้แน่ใจว่าผู้ใช้ไม่ได้โต้ตอบ ในทางกลับกัน เบราว์เซอร์จะทราบเวลาที่เหลืออยู่เมื่อสิ้นสุดเฟรม และทราบว่าผู้ใช้กำลังโต้ตอบอยู่หรือไม่ ดังนั้น requestIdleCallback จึงทำให้เราได้รับ API ที่ช่วยให้ใช้ประโยชน์จากเวลาที่เหลืออยู่ได้อย่างมีประสิทธิภาพมากที่สุด
มาดูรายละเอียดเพิ่มเติมและวิธีใช้ประโยชน์กัน
กำลังตรวจสอบ requestIdleCallback
requestIdleCallback เพิ่งเปิดตัวไปเมื่อไม่นานมานี้ คุณจึงควรตรวจสอบความพร้อมใช้งานก่อนใช้งาน
if ('requestIdleCallback' in window) {
// Use requestIdleCallback to schedule work.
} else {
// Do what you’d do today.
}
นอกจากนี้ คุณยังปรับลักษณะการทํางานของ setTimeout ได้ด้วย โดยจะต้องเปลี่ยนไปใช้ setTimeout ดังนี้
window.requestIdleCallback =
window.requestIdleCallback ||
function (cb) {
var start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
}
});
}, 1);
}
window.cancelIdleCallback =
window.cancelIdleCallback ||
function (id) {
clearTimeout(id);
}
การใช้ setTimeout นั้นไม่ค่อยดีนักเนื่องจาก setTimeout ไม่รู้เกี่ยวกับเวลาที่ไม่ได้ใช้งาน ต่างจาก requestIdleCallback แต่เนื่องจากคุณจะเรียกใช้ฟังก์ชันโดยตรงหาก requestIdleCallback ไม่พร้อมใช้งาน คุณจึงใช้วิธีนี้แทนได้ เมื่อใช้ชิมนี้ หาก requestIdleCallback พร้อมใช้งาน ระบบจะเปลี่ยนเส้นทางการโทรของคุณโดยอัตโนมัติ ซึ่งยอดเยี่ยมมาก
แต่ตอนนี้เรามาสมมติว่าไฟล์มีอยู่แล้ว
การใช้ requestIdleCallback
การเรียกใช้ requestIdleCallback คล้ายกับ requestAnimationFrame ตรงที่รับฟังก์ชัน Callback เป็นพารามิเตอร์แรก
requestIdleCallback(myNonEssentialWork);
เมื่อเรียก myNonEssentialWork ระบบจะให้ออบเจ็กต์ deadline ซึ่งมีฟังก์ชันที่แสดงผลตัวเลขซึ่งระบุเวลาที่เหลือสำหรับงานของคุณ
function myNonEssentialWork (deadline) {
while (deadline.timeRemaining() > 0)
doWorkIfNeeded();
}
เรียกใช้ฟังก์ชัน timeRemaining เพื่อรับค่าล่าสุดได้ เมื่อ timeRemaining() แสดงผลเป็น 0 คุณสามารถกําหนดเวลา requestIdleCallback ใหม่ได้หากยังมีงานเหลืออยู่ โดยทําดังนี้
function myNonEssentialWork (deadline) {
while (deadline.timeRemaining() > 0 && tasks.length > 0)
doWorkIfNeeded();
if (tasks.length > 0)
requestIdleCallback(myNonEssentialWork);
}
การรับประกันว่ามีการเรียกใช้ฟังก์ชัน
คุณจะทำอย่างไรหากมีงานล้นมือ คุณอาจกังวลว่าอาจไม่มีใครโทรกลับหาคุณ แม้ว่า requestIdleCallback จะคล้ายกับ requestAnimationFrame แต่ก็มีความแตกต่างตรงที่รับพารามิเตอร์ที่ 2 ซึ่งเป็นออบเจ็กต์ตัวเลือกที่มีพร็อพเพอร์ตี้ timeout การหมดเวลานี้ (หากตั้งค่าไว้) จะให้เวลาเบราว์เซอร์เป็นมิลลิวินาทีในการเรียกใช้การเรียกกลับ
// Wait at most two seconds before processing events.
requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
หากการเรียกกลับทํางานเนื่องจากมีการเรียกใช้การหมดเวลา คุณจะเห็น 2 สิ่งต่อไปนี้
timeRemaining()จะแสดงผลเป็น 0- พร็อพเพอร์ตี้
didTimeoutของออบเจ็กต์deadlineจะเท่ากับ true
หากพบว่า didTimeout เป็น "จริง" คุณก็อาจต้องการเรียกใช้งานให้เสร็จสิ้น
function myNonEssentialWork (deadline) {
// Use any remaining time, or, if timed out, just run through the tasks.
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&
tasks.length > 0)
doWorkIfNeeded();
if (tasks.length > 0)
requestIdleCallback(myNonEssentialWork);
}
โปรดระมัดระวังในการตั้งค่าพารามิเตอร์นี้เนื่องจากอาจทำให้ผู้ใช้ของคุณไม่ได้รับความสะดวก (การดำเนินการอาจทําให้แอปไม่ตอบสนองหรือทำงานขัดข้อง) ในกรณีที่ทำได้ ให้เบราว์เซอร์ตัดสินใจว่าจะเรียกใช้การโทรกลับเมื่อใด
การใช้ requestIdleCallback สําหรับการส่งข้อมูลวิเคราะห์
มาดูการใช้ requestIdleCallback เพื่อส่งข้อมูลวิเคราะห์กัน ในกรณีนี้ เราอาจต้องการติดตามเหตุการณ์ เช่น การแตะเมนูการนำทาง อย่างไรก็ตาม เนื่องจากปกติแล้วโฆษณาจะเคลื่อนไหวบนหน้าจอ เราจึงต้องการหลีกเลี่ยงการส่งเหตุการณ์นี้ไปยัง Google Analytics ทันที เราจะสร้างอาร์เรย์ของเหตุการณ์ที่จะส่งและส่งคำขอให้ส่งเหตุการณ์ดังกล่าวในอนาคต
var eventsToSend = [];
function onNavOpenClick () {
// Animate the menu.
menu.classList.add('open');
// Store the event for later.
eventsToSend.push(
{
category: 'button',
action: 'click',
label: 'nav',
value: 'open'
});
schedulePendingEvents();
}
ตอนนี้เราจะต้องใช้ requestIdleCallback เพื่อประมวลผลเหตุการณ์ที่รอดำเนินการ
function schedulePendingEvents() {
// Only schedule the rIC if one has not already been set.
if (isRequestIdleCallbackScheduled)
return;
isRequestIdleCallbackScheduled = true;
if ('requestIdleCallback' in window) {
// Wait at most two seconds before processing events.
requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
} else {
processPendingAnalyticsEvents();
}
}
คุณจะเห็นว่าเราได้ตั้งค่าการหมดเวลาเป็น 2 วินาที แต่ค่านี้จะขึ้นอยู่กับแอปพลิเคชันของคุณ สําหรับข้อมูลวิเคราะห์ การใช้การหมดเวลาเพื่อให้มั่นใจว่าข้อมูลได้รับการรายงานในกรอบเวลาที่สมเหตุสมผลแทนที่จะรายงานเมื่อใดก็ได้ในอนาคตนั้นเป็นสิ่งที่ควรทำ
สุดท้ายเราต้องเขียนฟังก์ชันที่ requestIdleCallback จะเรียกใช้
function processPendingAnalyticsEvents (