JavaScript API ใหม่ที่อาจช่วยคุณหลีกเลี่ยงการเลือกระหว่างประสิทธิภาพในการโหลดและการตอบสนองของอินพุต
การโหลดเร็วเป็นเรื่องยาก เว็บไซต์ที่ใช้ประโยชน์จาก JS ในการแสดงผลเนื้อหาในปัจจุบันต้องให้สมดุลระหว่างประสิทธิภาพการโหลดกับการตอบสนองของอินพุต โดยทำงานทั้งหมดที่จำเป็นสำหรับการแสดงผลพร้อมกัน (ประสิทธิภาพในการโหลดที่ดีขึ้น การตอบสนองของอินพุตแย่กว่า) หรือแบ่งงานออกเป็นงานเล็กๆ เพื่อให้ตอบสนองต่อการป้อนข้อมูลและระบายสี (ประสิทธิภาพในการโหลดที่แย่กว่า การตอบสนองของอินพุตดีขึ้น)
Facebook ได้เสนอและใช้ isInputPending()
API ใน Chromium เพื่อปรับปรุงการตอบสนองโดยไม่ต้องให้ผลตอบแทน เพื่อลดความจำเป็นในการต่อรองนี้ จากความคิดเห็นของช่วงทดลองใช้จากต้นทาง เราได้ทำการอัปเดต API จำนวนหนึ่ง และยินดีที่จะประกาศว่าขณะนี้ API จะจัดส่งโดยค่าเริ่มต้นใน Chromium 87!
ความเข้ากันได้กับเบราว์เซอร์
การสนับสนุนเบราว์เซอร์
- 87
- 87
- x
- x
isInputPending()
จะจัดส่งในเบราว์เซอร์แบบ Chromium โดยเริ่มตั้งแต่เวอร์ชัน 87 เป็นต้นไป
ไม่มีเบราว์เซอร์อื่นที่ส่งสัญญาณแจ้งความต้องการที่จะจัดส่ง API
ที่มา
งานส่วนใหญ่ในระบบนิเวศ JS ปัจจุบันจะทำงานเสร็จในเทรดเดียว ซึ่งก็คือเทรดหลัก สิ่งนี้มอบโมเดลการดำเนินการที่มีประสิทธิภาพแก่นักพัฒนาซอฟต์แวร์ แต่ประสบการณ์ของผู้ใช้ (โดยเฉพาะการตอบสนอง) อาจได้รับผลกระทบอย่างมากหากสคริปต์ทำงานเป็นเวลานาน เช่น หากหน้าเว็บทำงานหนักขณะที่เหตุการณ์อินพุตเริ่มทำงาน หน้าเว็บจะไม่จัดการกับเหตุการณ์การคลิกอินพุตจนกว่าการทำงานนั้นจะเสร็จสมบูรณ์
แนวทางปฏิบัติแนะนำในปัจจุบันคือการจัดการปัญหานี้โดยการแบ่ง JavaScript ออกเป็นบล็อกเล็กๆ ขณะที่หน้าเว็บกำลังโหลด หน้าเว็บสามารถเรียกใช้ JavaScript สักเล็กน้อย แล้วแสดงผลและส่งการควบคุมกลับไปยังเบราว์เซอร์ จากนั้น เบราว์เซอร์จะสามารถตรวจสอบคิวกิจกรรมอินพุตของตนและดูว่ามีอะไรที่ต้องบอกเกี่ยวกับหน้าเว็บหรือไม่ จากนั้น เบราว์เซอร์จะสามารถกลับไปเรียกใช้บล็อก JavaScript ที่เพิ่มเข้ามาได้ วิธีนี้ช่วยได้ แต่ก็อาจทำให้เกิดปัญหาอื่นๆ ได้เช่นกัน
แต่ละครั้งที่หน้าเว็บให้การควบคุมกลับไปยังเบราว์เซอร์ เบราว์เซอร์จะต้องใช้เวลาสักครู่เพื่อตรวจสอบคิวเหตุการณ์อินพุต ประมวลผลเหตุการณ์ และเลือกบล็อก JavaScript ถัดไป ขณะที่เบราว์เซอร์ตอบสนองต่อเหตุการณ์ต่างๆ ได้เร็วขึ้น เวลาโหลดโดยรวมของหน้าเว็บจะช้าลง และถ้าเราแสดงบ่อยๆ หน้าเว็บ ก็จะโหลดช้าเกินไป หากเราแสดงผลไม่บ่อย เบราว์เซอร์ก็จะตอบสนองต่อเหตุการณ์ของผู้ใช้นานขึ้นและผู้คนก็หงุดหงิด ไม่สนุกนะ
ที่ Facebook เราอยากรู้ว่าจะเกิดอะไรขึ้นหากเราคิดค้นวิธีใหม่สำหรับการโหลดที่จะช่วยขจัดอุปสรรคที่น่าหงุดหงิดนี้ เราได้ติดต่อกับเพื่อนๆ ที่ Chrome เกี่ยวกับเรื่องนี้และได้ยื่นข้อเสนอสำหรับ isInputPending()
isInputPending()
API เป็นวิธีแรกที่ใช้แนวคิดการรบกวนอินพุตของผู้ใช้ในเว็บ และช่วยให้ JavaScript ตรวจสอบอินพุตได้โดยไม่ต้องผ่านเบราว์เซอร์
เนื่องจากผู้ใช้สนใจ API ดังกล่าว เราจึงร่วมมือกับเพื่อนร่วมงานที่ Chrome เพื่อใช้งานและจัดส่งฟีเจอร์นี้ใน Chromium ด้วยความช่วยเหลือจากวิศวกร Chrome เราจึงได้รับแพตช์จากช่วงทดลองใช้จากต้นทาง (ซึ่งเป็นวิธีที่ Chrome จะใช้ทดสอบการเปลี่ยนแปลงและรับความคิดเห็นจากนักพัฒนาแอปก่อนที่จะเผยแพร่ API อย่างเต็มรูปแบบ)
ตอนนี้เราได้รับความคิดเห็นจากช่วงทดลองใช้จากต้นทางและจากสมาชิกคนอื่นๆ ในคณะทำงานของ W3C Web Performance และนำการเปลี่ยนแปลงไปใช้กับ API แล้ว
ตัวอย่าง: เครื่องจัดตารางเวลาผลตอบแทน
สมมติว่าคุณมีการบล็อกโฆษณาแบบดิสเพลย์จำนวนมากที่ต้องทำเพื่อโหลดหน้าเว็บ เช่น การสร้างมาร์กอัปจากคอมโพเนนต์ แยกตัวประกอบ หรือแค่วาดตัวหมุนแสดงการโหลดเจ๋งๆ แต่ละเครื่องมือจะแยกออกเป็น
รายการงานแยกกัน มาลองร่างวิธีที่เราอาจประมวลผลงานในฟังก์ชัน processWorkQueue()
สมมติโดยใช้รูปแบบเครื่องจัดตารางเวลา ดังนี้
const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
if (performance.now() >= DEADLINE) {
// Yield the event loop if we're out of time.
setTimeout(processWorkQueue);
return;
}
let job = workQueue.shift();
job.execute();
}
การเรียกใช้ processWorkQueue()
ภายหลังในมาโครงานใหม่ผ่าน setTimeout()
ทำให้เบราว์เซอร์สามารถตอบสนองต่ออินพุตได้ค่อนข้างดี (โดยสามารถเรียกใช้เครื่องจัดการเหตุการณ์ก่อนที่งานจะกลับมาทำงานอีกครั้ง) ในขณะที่ยังสามารถเรียกใช้ได้โดยไม่ถูกขัดจังหวะ อย่างไรก็ตาม เราอาจเลื่อนเวลาออกไปเป็นเวลานานเนื่องจากมีงานอื่นๆ ที่ต้องการควบคุมเหตุการณ์วนซ้ำ หรือได้รับเวลาในการตอบสนองเพิ่มขึ้นถึง QUANTUM
มิลลิวินาที
ไม่เป็นไร แต่เราจะทำให้ดีขึ้นได้หรือไม่ แน่นอนที่สุด
const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
// Yield if we have to handle an input event, or we're out of time.
setTimeout(processWorkQueue);
return;
}
let job = workQueue.shift();
job.execute();
}
การใช้การเรียก navigator.scheduling.isInputPending()
ช่วยให้เราตอบกลับอินพุตได้เร็วขึ้น ในขณะเดียวกันก็ยังทำให้งานที่บล็อกการแสดงผลดำเนินการได้อย่างไม่ขาดตอน หากเราไม่สนใจจัดการสิ่งต่างๆ นอกเหนือจากอินพุต (เช่น การวาดภาพ) จนกว่างานจะเสร็จสมบูรณ์ เราก็จะเพิ่มความยาวของ QUANTUM
ได้เช่นกัน
โดยค่าเริ่มต้น ระบบจะไม่แสดงผลเหตุการณ์ "ต่อเนื่อง" จาก isInputPending()
ซึ่งรวมถึง mousemove
, pointermove
และอื่นๆ หากคุณสนใจที่จะทำรายได้เหล่านี้
เช่นกัน ก็ไม่เป็นปัญหา เมื่อระบุออบเจ็กต์ไปยัง isInputPending()
โดยตั้งค่า includeContinuous
เป็น true
เราก็พร้อมจะทำสิ่งต่อไปนี้
const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
// Yield if we have to handle an input event (any of them!), or we're out of time.
setTimeout(processWorkQueue);
return;
}
let job = workQueue.shift();
job.execute();
}
เท่านี้ก็เรียบร้อย เฟรมเวิร์กอย่าง React กำลังสร้างการรองรับ isInputPending()
ในไลบรารีการกำหนดเวลาหลักโดยใช้ตรรกะที่คล้ายกัน ซึ่งเราหวังว่าจะช่วยให้นักพัฒนาซอฟต์แวร์ที่ใช้เฟรมเวิร์กเหล่านี้ได้รับประโยชน์จากการทำงานเบื้องหลังของ isInputPending()
โดยไม่ต้องทำการรีไรท์มากนัก
ผลตอบแทนไม่ได้แย่เสมอไป
โปรดทราบว่าผลตอบแทนที่น้อยลงไม่ใช่วิธีแก้ปัญหาที่เหมาะสมสำหรับกรณีการใช้งานทุกกรณี มีหลายเหตุผลที่จะคืนการควบคุมกลับไปยังเบราว์เซอร์นอกเหนือจากการประมวลผลเหตุการณ์อินพุต เช่น เพื่อแสดงผลและเรียกใช้สคริปต์อื่นๆ ในหน้าเว็บ
มีบางกรณีที่เบราว์เซอร์ระบุแหล่งที่มาของเหตุการณ์อินพุตที่รอดำเนินการไม่ได้ โดยเฉพาะอย่างยิ่ง การตั้งค่าคลิปและมาสก์ที่ซับซ้อนสำหรับ iframe แบบข้ามต้นทางอาจรายงานผลลบลวง (เช่น isInputPending()
อาจแสดงผลเป็น "เท็จ" โดยไม่คาดคิดเมื่อกำหนดเป้าหมายเฟรมเหล่านี้) ตรวจดูว่าคุณได้ผลตอบแทนบ่อยพอหากเว็บไซต์จำเป็นต้องมีการโต้ตอบกับเฟรมย่อยที่มีสไตล์
โปรดระวังหน้าเว็บอื่นๆ ที่แชร์การวนซ้ำเหตุการณ์ด้วยเช่นกัน ในแพลตฟอร์มอย่าง Chrome สำหรับ Android นั้น การที่หลายๆ ต้นทางแชร์ลูปเหตุการณ์เป็นเรื่องปกติ isInputPending()
จะไม่แสดงผล true
หากอินพุตส่งไปยังเฟรมแบบข้ามต้นทาง หน้าที่อยู่เบื้องหลังจึงอาจรบกวนการตอบสนองของหน้าเบื้องหน้า คุณอาจต้องการลด เลื่อน หรือให้ผลการค้นหาบ่อยขึ้นเมื่อทำงานในเบื้องหลังโดยใช้ API สำหรับระดับการเข้าถึงหน้าเว็บ
เราขอแนะนำให้คุณใช้ isInputPending()
ตามที่เห็นสมควร หากไม่มีการดำเนินการบล็อกผู้ใช้ โปรดแสดงเมตตาต่อคนอื่นๆ ในลูปเหตุการณ์โดยให้ชิ้นงานบ่อยขึ้น งานที่ใช้เวลานานอาจเป็นอันตรายได้
ความคิดเห็น
- แสดงความคิดเห็นเกี่ยวกับข้อกำหนดในที่เก็บ is-input-pending
- ติดต่อ @acomminos (หนึ่งในผู้เขียนข้อกำหนด) บน Twitter
บทสรุป
เรารู้สึกตื่นเต้นที่ isInputPending()
กำลังจะเปิดตัวและนักพัฒนาซอฟต์แวร์สามารถเริ่มใช้งานได้แล้ววันนี้ API นี้เป็นครั้งแรกที่ Facebook สร้าง API เว็บแบบใหม่ โดยเปลี่ยนจากการบ่มแนวคิดไปสู่ข้อเสนอมาตรฐานไปจนถึงการจัดส่งจริงในเบราว์เซอร์ เราขอขอบคุณทุกคนที่ช่วยให้เรามาถึงจุดนี้
และขอขอบคุณทุกคนใน Chrome ที่ช่วยเราต่อยอดไอเดียนี้และส่งต่อไป
รูปภาพหลักโดย Will H McMahan ใน Unsplash