เพิ่มประสิทธิภาพงานที่ใช้เวลานาน

คุณเคยได้ยินว่า "อย่าบล็อกเธรดหลัก" และ "แบ่งงานที่มีระยะเวลานาน" แต่การทําเช่นนั้นหมายความว่าอย่างไร

เผยแพร่: 30 กันยายน 2022 อัปเดตล่าสุด: 19 ธันวาคม 2024

คำแนะนำทั่วไปในการทำให้แอป JavaScript ทำงานได้อย่างรวดเร็วมักจะสรุปเป็นคำแนะนำต่อไปนี้

  • "อย่าบล็อกชุดข้อความหลัก"
  • "แบ่งงานใหญ่ออกเป็นงานย่อยๆ"

นี่เป็นคำแนะนำที่ดีมาก แต่ต้องทํางานอะไรบ้าง การส่ง JavaScript น้อยลงนั้นเป็นเรื่องที่ดี แต่นั่นหมายความว่าอินเทอร์เฟซผู้ใช้จะตอบสนองได้ดีขึ้นโดยอัตโนมัติไหม อาจจะเป็นไปได้หรืออาจจะไม่

หากต้องการทำความเข้าใจวิธีเพิ่มประสิทธิภาพงานใน JavaScript ก่อนอื่นคุณต้องทราบว่างานคืออะไร และเบราว์เซอร์จัดการงานอย่างไร

งานคืออะไร

งานคืองานย่อยๆ ที่เบราว์เซอร์ทำ ซึ่งรวมถึงการแสดงผล การแยกวิเคราะห์ HTML และ CSS การรัน JavaScript และงานประเภทอื่นๆ ที่คุณอาจไม่สามารถควบคุมได้โดยตรง งานนี้ JavaScript ที่คุณเขียนอาจเป็นแหล่งที่มาของงานมากที่สุด

ภาพแสดงงานตามที่แสดงในเครื่องมือวัดประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome งานที่ด้านบนของกองมีตัวแฮนเดิลเหตุการณ์คลิก การเรียกฟังก์ชัน และรายการอื่นๆ อยู่ด้านล่าง งานนี้ยังมีงานแสดงผลบางอย่างทางด้านขวามือด้วย
งานที่เริ่มต้นโดยตัวแฮนเดิลเหตุการณ์ click ใน แสดงในเครื่องมือวิเคราะห์ประสิทธิภาพของเครื่องมือสําหรับนักพัฒนาเว็บใน Chrome

งานที่เกี่ยวข้องกับ JavaScript จะส่งผลต่อประสิทธิภาพใน 2 วิธีดังนี้

  • เมื่อเบราว์เซอร์ดาวน์โหลดไฟล์ JavaScript ระหว่างการเริ่มต้นระบบ เบราว์เซอร์จะจัดคิวงานเพื่อแยกวิเคราะห์และคอมไพล์ JavaScript นั้นเพื่อให้สามารถเรียกใช้ได้ในภายหลัง
  • ในช่วงเวลาอื่นๆ ตลอดอายุการใช้งานของหน้าเว็บ ระบบจะจัดคิวงานเมื่อ JavaScript ทํางาน เช่น การตอบสนองต่อการโต้ตอบผ่านตัวแฮนเดิลเหตุการณ์ ภาพเคลื่อนไหวที่ขับเคลื่อนโดย JavaScript และกิจกรรมเบื้องหลัง เช่น การเก็บรวบรวมข้อมูลวิเคราะห์

การดำเนินการทั้งหมดนี้ (ยกเว้น Web Worker และ API ที่คล้ายกัน) จะเกิดขึ้นในเธรดหลัก

เทรดหลักคืออะไร

เธรดหลักคือที่ที่งานส่วนใหญ่ทำงานในเบราว์เซอร์ และที่ที่ JavaScript เกือบทั้งหมดที่คุณเขียนจะทำงาน

เทรดหลักจะประมวลผลงานได้ครั้งละ 1 งานเท่านั้น งานใดก็ตามที่ใช้เวลานานกว่า 50 มิลลิวินาทีถือเป็นงานที่ใช้เวลานาน สำหรับงานที่ใช้เวลานานกว่า 50 มิลลิวินาที เวลาทั้งหมดของงานลบด้วย 50 มิลลิวินาทีเรียกว่าระยะเวลาการบล็อกของงาน

เบราว์เซอร์จะบล็อกไม่ให้เกิดการโต้ตอบขณะที่งานกำลังทำงานอยู่ไม่ว่างานจะยาวแค่ไหนก็ตาม แต่ผู้ใช้จะไม่สังเกตเห็นการทำงานนี้ตราบใดที่งานไม่ทำงานนานเกินไป อย่างไรก็ตาม เมื่อผู้ใช้พยายามโต้ตอบกับหน้าเว็บเมื่อมีงานจำนวนมากที่ใช้เวลานาน อินเทอร์เฟซผู้ใช้จะดูเหมือนไม่ตอบสนองและอาจใช้งานไม่ได้หากมีการบล็อกเธรดหลักเป็นเวลานาน

งานที่มีระยะเวลานานในเครื่องมือวิเคราะห์ประสิทธิภาพของ DevTools ใน Chrome ส่วนการบล็อกของงาน (มากกว่า 50 มิลลิวินาที) จะแสดงด้วยลายทางทแยงมุมสีแดง
งานที่ใช้เวลานานตามที่แสดงในเครื่องมือวิเคราะห์ประสิทธิภาพของ Chrome งานที่มีความยาวจะแสดงด้วยรูปสามเหลี่ยมสีแดงที่มุมของงาน โดยส่วนที่บล็อกของงานจะมีลายเส้นสีแดงทแยงมุม

คุณสามารถแบ่งงานที่มีขนาดใหญ่ออกเป็นงานเล็กๆ หลายงานเพื่อป้องกันไม่ให้เทรดหลักถูกบล็อกเป็นเวลานาน

งานเดียวที่ยาวมากเทียบกับงานเดียวกันที่แบ่งออกเป็นงานสั้นๆ งานแบบยาวเป็นรูปสี่เหลี่ยมผืนผ้าขนาดใหญ่ 1 รูป ส่วนงานแบบแบ่งเป็นชิ้นๆ เป็นรูปสี่เหลี่ยมผืนผ้าขนาดเล็ก 5 รูปที่มีความกว้างรวมเท่ากับงานแบบยาว
การแสดงภาพงานยาวรายการเดียวเทียบกับงานเดียวกันที่แบ่งออกเป็นงานสั้นๆ 5 รายการ

การดำเนินการนี้สำคัญเนื่องจากเมื่อแบ่งงานออกเป็นหลายส่วนแล้ว เบราว์เซอร์จะตอบสนองต่องานที่มีความสำคัญมากกว่าได้เร็วขึ้นมาก รวมถึงการโต้ตอบของผู้ใช้ด้วย หลังจากนั้น ระบบจะเรียกใช้งานที่เหลือจนเสร็จสมบูรณ์ เพื่อให้งานที่คุณจัดคิวไว้ตั้งแต่แรกเสร็จสมบูรณ์

ภาพแสดงว่าการแบ่งงานช่วยให้ผู้ใช้โต้ตอบได้ง่ายขึ้นได้อย่างไร ที่ด้านบน งานระยะยาวจะบล็อกตัวแฮนเดิลเหตุการณ์ไม่ให้ทํางานจนกว่างานจะเสร็จ ที่ด้านล่าง งานที่มีการแบ่งออกเป็นหลายส่วนจะอนุญาตให้ตัวแฮนเดิลเหตุการณ์ทำงานได้เร็วขึ้น
ภาพแสดงสิ่งที่เกิดขึ้นกับการโต้ตอบเมื่องานมีความยาวเกินไปและเบราว์เซอร์ตอบสนองต่อการโต้ตอบไม่ทันเมื่อเทียบกับกรณีที่แบ่งงานที่มีความยาวออกเป็นงานย่อยๆ

ที่ด้านบนของรูปภาพก่อนหน้า ตัวแฮนเดิลเหตุการณ์ที่จัดคิวไว้โดยการโต้ตอบของผู้ใช้ต้องรองานระยะยาวรายการเดียวก่อนจึงจะเริ่มได้ ซึ่งทำให้การโต้ตอบล่าช้า ในกรณีนี้ ผู้ใช้อาจสังเกตเห็นความล่าช้า ที่ด้านล่าง ตัวแฮนเดิลเหตุการณ์จะเริ่มทํางานเร็วขึ้น และผู้ใช้อาจรู้สึกว่าการโต้ตอบนั้นเกิดขึ้นทันที

เมื่อทราบความสำคัญของการแบ่งงานแล้ว คุณก็ดูวิธีดำเนินการใน JavaScript ได้

กลยุทธ์การจัดการงาน

คำแนะนำทั่วไปในสถาปัตยกรรมซอฟต์แวร์คือการแบ่งงานออกเป็นฟังก์ชันย่อยๆ ดังนี้

function saveSettings () {
  validateForm();
  showSpinner();
  saveToDatabase();
  updateUI();
  sendAnalytics();
}

ในตัวอย่างนี้ มีฟังก์ชันชื่อ saveSettings() ที่เรียกใช้ฟังก์ชัน 5 รายการเพื่อตรวจสอบแบบฟอร์ม แสดงภาพสปินเนอร์ ส่งข้อมูลไปยังแบ็กเอนด์ของแอปพลิเคชัน อัปเดตอินเทอร์เฟซผู้ใช้ และส่งข้อมูลวิเคราะห์

saveSettings() มีสถาปัตยกรรมที่ดีในแง่แนวคิด หากต้องการแก้ไขข้อบกพร่องของฟังก์ชันใดฟังก์ชันหนึ่งเหล่านี้ คุณสามารถไปยังส่วนต่างๆ ของลําดับชั้นโปรเจ็กต์เพื่อดูว่าฟังก์ชันแต่ละรายการทํางานอย่างไร การแบ่งงานเช่นนี้จะช่วยให้คุณไปยังส่วนต่างๆ ของโปรเจ็กต์และดูแลรักษาโปรเจ็กต์ได้ง่ายขึ้น

อย่างไรก็ตาม ปัญหาที่อาจเกิดขึ้นคือ JavaScript จะไม่เรียกใช้ฟังก์ชันเหล่านี้แต่ละรายการเป็นงานแยกต่างหาก เนื่องจากจะดำเนินการภายในฟังก์ชัน saveSettings() ซึ่งหมายความว่าฟังก์ชันทั้ง 5 รายการจะทํางานเป็นงานเดียว

ฟังก์ชัน saveSettings ตามที่แสดงในเครื่องมือวิเคราะห์ประสิทธิภาพของ Chrome ขณะที่ฟังก์ชันระดับบนสุดเรียกใช้ฟังก์ชันอื่นๆ อีก 5 รายการ งานทั้งหมดจะเกิดขึ้นในงานยาวๆ งานเดียว ซึ่งทำให้ผู้ใช้ไม่เห็นผลลัพธ์ที่มองเห็นได้ของการทำงานฟังก์ชันจนกว่างานทั้งหมดจะเสร็จสมบูรณ์
ฟังก์ชันเดียว saveSettings() ที่เรียกใช้ฟังก์ชัน 5 รายการ งานจะทํางานเป็นส่วนหนึ่งของงานแบบโมโนลิธิกงานเดียวที่ยาวนาน ซึ่งจะบล็อกการตอบสนองภาพจนกว่าฟังก์ชันทั้ง 5 รายการจะเสร็จสมบูรณ์

ในกรณีที่ดีที่สุด ฟังก์ชันเพียงฟังก์ชันเดียวอาจทำให้งานใช้เวลานานขึ้น 50 มิลลิวินาทีหรือมากกว่านั้น ในกรณีที่แย่ที่สุด งานเหล่านี้อาจทำงานได้นานขึ้นมาก โดยเฉพาะในอุปกรณ์ที่มีทรัพยากรจํากัด

ในกรณีนี้ saveSettings() จะทริกเกอร์โดยการคลิกของผู้ใช้ และเนื่องจากเบราว์เซอร์ไม่สามารถแสดงการตอบกลับจนกว่าฟังก์ชันทั้งหมดจะทำงานเสร็จสิ้น ผลลัพธ์ของงานที่ใช้เวลานานนี้คือ UI ที่ช้าและไม่ตอบสนอง และระบบจะวัดเป็น Interaction to Next Paint (INP) ที่ไม่ดี

เลื่อนเวลาการเรียกใช้โค้ดด้วยตนเอง

คุณสามารถให้สิทธิ์เธรดหลักได้โดยหยุดงานชั่วคราวเพื่อให้เบราว์เซอร์มีโอกาสทำงานที่สำคัญกว่า เพื่อให้แน่ใจว่างานที่สำคัญซึ่งแสดงต่อผู้ใช้และการตอบสนองของ UI จะทำงานก่อนงานที่มีลำดับความสำคัญต่ำกว่า

วิธีหนึ่งที่นักพัฒนาแอปใช้เพื่อแบ่งงานออกเป็นงานเล็กๆ คือการใช้ setTimeout() เทคนิคนี้ช่วยให้คุณส่งฟังก์ชันไปยัง setTimeout() ได้ ซึ่งจะเลื่อนการดำเนินการของคอลแบ็กไปเป็นงานแยกต่างหาก แม้ว่าคุณจะระบุการหมดเวลาเป็น 0 ก็ตาม

function saveSettings () {
  // Do critical work that is user-visible:
  validateForm();
  showSpinner();
  updateUI();

  // Defer work that isn't user-visible to a separate task:
  setTimeout(() => {
    saveToDatabase();
    sendAnalytics();
  }, 0);
}

การดำเนินการนี้เรียกว่าการให้ผล และเหมาะสําหรับชุดฟังก์ชันที่ต้องทํางานตามลําดับ

อย่างไรก็ตาม รหัสของคุณอาจไม่ได้จัดเรียงแบบนี้เสมอไป เช่น คุณอาจมีข้อมูลจํานวนมากที่ต้องประมวลผลในลูป และงานดังกล่าวอาจใช้เวลานานมากหากมีการทําซ้ำหลายครั้ง

function processData () {
  for (const item of largeDataArray) {
    // Process the individual item here.
  }
}

การใช้ setTimeout() ที่นี่มีปัญหาเนื่องจากความสะดวกสบายของนักพัฒนาซอฟต์แวร์ และหลังจากใช้ setTimeout() ที่ฝังซ้อนกัน 5 รอบ เบราว์เซอร์จะเริ่มหน่วงเวลาอย่างน้อย 5 มิลลิวินาทีสําหรับ setTimeout() แต่ละรายการที่เพิ่มเข้ามา

setTimeout ยังมีข้อเสียอีกอย่างหนึ่งเกี่ยวกับการให้สิทธิ์ใช้ทรัพยากร: เมื่อคุณให้สิทธิ์ใช้ทรัพยากรแก่เธรดหลักโดยการเลื่อนโค้ดให้ทำงานในงานถัดไปโดยใช้ setTimeout ระบบจะเพิ่มงานนั้นไว้ที่ท้ายของคิว หากมีงานอื่นๆ ที่รออยู่ งานเหล่านั้นจะทำงานก่อนโค้ดที่เลื่อน

API ที่ให้ผลตอบแทนโดยเฉพาะ: scheduler.yield()

Browser Support

  • Chrome: 129.
  • Edge: 129.
  • Firefox: not supported.
  • Safari: not supported.

Source

scheduler.yield() เป็น API ที่ออกแบบมาเพื่อส่งมอบชุดข้อความหลักในเบราว์เซอร์โดยเฉพาะ

scheduler.yield() ไม่ใช่ไวยากรณ์ระดับภาษาหรือโครงสร้างพิเศษ เป็นเพียงฟังก์ชันที่แสดงผล Promise ซึ่งจะได้รับการแก้ไขในภารกิจในอนาคต โค้ดใดก็ตามที่ลิงก์ไว้ให้ทำงานหลังจาก Promise ได้รับการแก้ไข (ในเชน .then() ที่ชัดเจนหรือหลังจาก await ในฟังก์ชันการทำงานแบบแอสซิงค์) จะทำงานในภารกิจในอนาคต

ในทางปฏิบัติ ให้แทรก await scheduler.yield() แล้วฟังก์ชันจะหยุดการดําเนินการชั่วคราว ณ จุดนั้นและส่งมอบการควบคุมไปยังเธรดหลัก ระบบจะกำหนดเวลาให้การดำเนินการของฟังก์ชันที่เหลือซึ่งเรียกว่าการดำเนินการต่อของฟังก์ชันนั้นทำงานในภารกิจลูปเหตุการณ์ใหม่ เมื่องานนั้นเริ่มต้นขึ้น ระบบจะแก้ไข Promise ที่รออยู่ และฟังก์ชันจะดำเนินการต่อจากจุดที่หยุดไว้

async function saveSettings () {
  // Do critical work that is user-visible:
  validateForm();
  showSpinner();
  updateUI();

  // Yield to the main thread:
  await scheduler.yield()

  // Work that isn't user-visible, continued in a separate task:
  saveToDatabase();
  sendAnalytics();
}
ฟังก์ชัน saveSettings ที่แสดงในเครื่องมือวิเคราะห์ประสิทธิภาพของ Chrome แบ่งออกเป็น 2 งานแล้ว งานที่ 1 จะเรียกใช้ฟังก์ชัน 2 รายการ จากนั้นจะแสดงผลเพื่อให้เลย์เอาต์และ Paint ทำงานและแสดงการตอบกลับที่มองเห็นได้ต่อผู้ใช้ เหตุการณ์คลิกจึงเสร็จสมบูรณ์ใน 64 มิลลิวินาที ซึ่งเร็วกว่ามาก งานที่สองจะเรียกใช้ฟังก์ชัน 3 รายการสุดท้าย
ตอนนี้การเรียกใช้ฟังก์ชัน saveSettings() จะแยกออกเป็น 2 งาน ด้วยเหตุนี้ เลย์เอาต์และการวาดจึงทำงานระหว่างงานต่างๆ ได้ ซึ่งช่วยให้ผู้ใช้เห็นการตอบสนองที่เร็วขึ้นดังที่วัดจากการโต้ตอบด้วยเคอร์เซอร์ที่สั้นลงมาก

อย่างไรก็ตาม ประโยชน์ที่แท้จริงของ scheduler.yield() เหนือกว่าแนวทางอื่นๆ ที่ให้ผลคือระบบจะให้ความสำคัญกับการดําเนินการต่อ ซึ่งหมายความว่าหากคุณ yield ในระหว่างที่ทํางานอยู่ การดำเนินการต่อของงานปัจจุบันจะทำงานก่อนที่จะเริ่มงานอื่นๆ ที่คล้ายกัน

วิธีนี้จะช่วยป้องกันไม่ให้โค้ดจากแหล่งที่มาของงานอื่นๆ ขัดจังหวะลําดับการเรียกใช้โค้ด เช่น งานจากสคริปต์ของบุคคลที่สาม

แผนภาพ 3 แผนภาพแสดงงานที่ไม่มีเงื่อนไขการให้ผล เงื่อนไขการให้ผล และเงื่อนไขการให้ผลและการดําเนินการต่อ หากไม่มีการให้สิทธิ์ใช้งาน อาจมีงานที่ใช้เวลานาน เมื่อใช้ Yield จะมีงานจำนวนมากขึ้นที่ใช้เวลาสั้นลง แต่อาจถูกขัดจังหวะโดยงานอื่นๆ ที่ไม่เกี่ยวข้อง เมื่อใช้ Yielding และ Continuation จะมีงานจำนวนมากขึ้นที่สั้นลง แต่ลำดับการดำเนินการจะยังคงเดิม
เมื่อใช้ scheduler.yield() การดําเนินการต่อจะเริ่มต้นจากจุดที่หยุดไว้ก่อนไปยังงานอื่นๆ

การรองรับข้ามเบราว์เซอร์

scheduler.yield() ยังไม่รองรับในบางเบราว์เซอร์ จึงต้องมีทางเลือกสำรอง

วิธีหนึ่งคือวาง scheduler-polyfill ลงในบิลด์ จากนั้นจะใช้ scheduler.yield() ได้โดยตรง โพลีฟีลจะจัดการกับการเปลี่ยนกลับไปใช้ฟังก์ชันการตั้งเวลางานอื่นๆ เพื่อให้ทำงานในเบราว์เซอร์ต่างๆ คล้ายกันได้

หรือจะเขียนเวอร์ชันที่ซับซ้อนน้อยกว่าในไม่กี่บรรทัดก็ได้ โดยใช้เพียง setTimeout ที่รวมอยู่ใน Promise เป็นการสำรองในกรณีที่ scheduler.yield() ไม่พร้อมใช้งาน

function yieldToMain () {
  if (globalThis.scheduler?.yield) {
    return scheduler.yield();
  }

  // Fall back to yielding with setTimeout.
  return new Promise(resolve => {
    setTimeout(resolve, 0);
  });
}

แม้ว่าเบราว์เซอร์ที่ไม่รองรับ scheduler.yield() จะไม่ได้รับการดําเนินการต่อแบบมีลําดับความสําคัญ แต่เบราว์เซอร์จะยังคงตอบสนองต่อไป

สุดท้ายนี้ อาจมีกรณีที่โค้ดของคุณไม่สามารถให้สิทธิ์แก่เธรดหลักได้หากไม่ได้ให้ความสำคัญกับการดําเนินการต่อ (เช่น หน้าเว็บที่ทราบว่ามีการใช้งานอยู่ซึ่งการให้สิทธิ์อาจทำให้งานไม่เสร็จสมบูรณ์เป็นระยะเวลาหนึ่ง) ในกรณีนี้ scheduler.yield() อาจได้รับการพิจารณาว่าเป็นการเพิ่มประสิทธิภาพแบบค่อยเป็นค่อยไป กล่าวคือ แสดงผลในเบราว์เซอร์ที่รองรับ scheduler.yield() หากไม่รองรับ ให้แสดงผลต่อไป

ซึ่งทำได้ทั้งการตรวจหาฟีเจอร์และการเปลี่ยนไปรองานย่อยรายการเดียวในบรรทัดเดียวที่มีประโยชน์

// Yield to the main thread if scheduler.yield() is available.
await globalThis.scheduler?.yield?.();

แบ่งงานระยะยาวด้วย scheduler.yield()

ข้อดีของการใช้ scheduler.yield() ด้วยวิธีใดวิธีหนึ่งเหล่านี้คือคุณสามารถ await ได้ในฟังก์ชัน async ใดก็ได้

เช่น หากมีงานจำนวนมากที่ต้องทำซึ่งมักจะกลายเป็นงานที่มีขนาดใหญ่ คุณสามารถแทรก yield เพื่อแบ่งงาน

async function runJobs(jobQueue) {
  for (const job of jobQueue) {
    // Run the job:
    job();

    // Yield to the main thread:
    await yieldToMain();
  }
}

ระบบจะจัดลําดับความสําคัญของ runJobs() ต่อ แต่ยังคงอนุญาตให้งานที่มีลําดับความสําคัญสูงกว่าทํางานได้ เช่น การตอบสนองต่ออินพุตของผู้ใช้ด้วยภาพ โดยไม่ต้องรอให้งานจํานวนมากทําเสร็จ

แต่วิธีนี้ใช้ประสิทธิภาพของอัตราผลตอบแทนอย่างไม่คุ้มค่า scheduler.yield() รวดเร็วและมีประสิทธิภาพ แต่ก็มีค่าใช้จ่ายเพิ่มเติมอยู่บ้าง หากงานบางรายการใน jobQueue สั้นมาก ค่าใช้จ่ายเพิ่มเติมอาจเพิ่มขึ้นอย่างรวดเร็วจนทำให้เวลาในการให้ผลลัพธ์และกลับมาทำงานต่อนานกว่าเวลาที่ใช้ทำงานจริง

วิธีหนึ่งคือการจัดกลุ่มงาน โดยแสดงผลระหว่างงานต่างๆ เฉพาะในกรณีที่ผ่านไปนานพอนับตั้งแต่การแสดงผลครั้งล่าสุด กำหนดเวลาทั่วไปคือ 50 มิลลิวินาทีเพื่อไม่ให้งานใช้เวลานาน แต่สามารถปรับเป็นค่ากลางระหว่างการตอบสนองกับเวลาที่ใช้ในการดำเนินการคิวงานให้เสร็จสมบูรณ์

async function runJobs(jobQueue, deadline=50) {
  let lastYield = performance.now();

  for (const job of jobQueue) {
    // Run the job:
    job();

    // If it's been longer than the deadline, yield to the main thread:
    if (performance.now() - lastYield > deadline) {
      await yieldToMain();
      lastYield = performance.now();
    }
  }
}

ผลที่ได้คือระบบจะแบ่งงานออกเป็นส่วนๆ เพื่อไม่ให้ใช้เวลานานเกินไป แต่ Runner จะส่งมอบให้กับเธรดหลักทุกๆ 50 มิลลิวินาทีเท่านั้น

ฟังก์ชันงานชุดหนึ่งที่แสดงในแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ซึ่งการดําเนินการจะแบ่งออกเป็นหลายงาน
งานที่จัดกลุ่มเป็นงานหลายรายการ

อย่าใช้ isInputPending()

Browser Support

  • Chrome: 87.
  • Edge: 87.
  • Firefox: not supported.
  • Safari: not supported.

Source

isInputPending() API มีวิธีตรวจสอบว่าผู้ใช้พยายามโต้ตอบกับหน้าเว็บหรือไม่ และจะแสดงผลก็ต่อเมื่อมีอินพุตที่รอดำเนินการ

ซึ่งจะช่วยให้ JavaScript ทำงานต่อไปได้หากไม่มีอินพุตที่รอดำเนินการแทนที่จะหยุดทำงานและไปอยู่ท้ายคิวงาน ซึ่งอาจส่งผลให้เกิดประสิทธิภาพที่ดีขึ้นอย่างน่าประทับใจ ตามที่ระบุไว้ในIntent to Ship สำหรับเว็บไซต์ที่อาจไม่ได้แสดงผลในเธรดหลัก

อย่างไรก็ตาม นับตั้งแต่เปิดตัว API ดังกล่าว ความเข้าใจของเราเกี่ยวกับการสร้างรายได้ก็เพิ่มขึ้น โดยเฉพาะเมื่อมีการนำ INP มาใช้ เราไม่แนะนำให้ใช้ API นี้อีกต่อไป และขอแนะนำให้ใช้ Yield ไม่ว่าอินพุตจะรอดำเนินการหรือไม่ก็ตาม เนื่องด้วยเหตุผลต่อไปนี้

  • isInputPending() อาจแสดงผล false อย่างไม่ถูกต้องแม้ว่าผู้ใช้จะโต้ตอบในบางสถานการณ์ก็ตาม
  • อินพุตไม่ใช่กรณีเดียวที่งานควรให้ผล ภาพเคลื่อนไหวและการอัปเดตอินเทอร์เฟซผู้ใช้ทั่วไปอื่นๆ มีความสำคัญไม่แพ้กับการสร้างหน้าเว็บที่ปรับเปลี่ยนตามอุปกรณ์
  • เราได้เปิดตัว API ที่ให้ผลลัพธ์ที่ครอบคลุมมากขึ้นซึ่งช่วยคลายข้อกังวลเกี่ยวกับผลลัพธ์ เช่น scheduler.postTask() และ scheduler.yield()

บทสรุป

การจัดการงานเป็นเรื่องท้าทาย แต่จะช่วยให้หน้าเว็บตอบสนองต่อการโต้ตอบของผู้ใช้ได้เร็วขึ้น การจัดการและจัดลําดับความสําคัญของงานนั้นไม่มีคําแนะนําเดียว แต่มีหลายเทคนิค เราขอทบทวนสิ่งสำคัญที่ควรพิจารณาเมื่อจัดการงานดังนี้

  • มอบสิทธิ์ให้ชุดข้อความหลักสำหรับงานที่สําคัญซึ่งแสดงต่อผู้ใช้
  • ใช้ scheduler.yield() (ที่มีทางเลือกสำรองสำหรับเบราว์เซอร์หลายประเภท) เพื่อให้ได้ประสิทธิภาพการทำงานที่เหมาะสมและรับการดําเนินการต่อที่มีลําดับความสําคัญ
  • สุดท้าย ทํางานในฟังก์ชันให้น้อยที่สุด

ดูข้อมูลเพิ่มเติมเกี่ยวกับ scheduler.yield(), scheduler.postTask() ที่เกี่ยวข้องซึ่งกำหนดเวลางานอย่างชัดเจน และการจัดลำดับความสำคัญของงานได้ที่เอกสาร Prioritized Task Scheduling API

เมื่อใช้เครื่องมือเหล่านี้อย่างน้อย 1 รายการ คุณควรจัดโครงสร้างงานในแอปพลิเคชันเพื่อให้จัดลําดับความสําคัญตามความต้องการของผู้ใช้ ในขณะเดียวกันก็ทํางานที่ไม่สําคัญมากนักได้ ซึ่งจะสร้างประสบการณ์การใช้งานที่ดีขึ้นซึ่งตอบสนองและใช้งานได้ง่ายขึ้น

ขอขอบคุณเป็นพิเศษ Philip Walton ที่ช่วยตรวจสอบคู่มือนี้ทางเทคนิค

ภาพปกจาก Unsplash โดยได้รับความอนุเคราะห์จาก Amirali Mirhashemian