ขอแนะนําช่วงทดลองใช้จากต้นทาง Scheduler.yield

การสร้างเว็บไซต์ที่ตอบสนองต่อข้อมูลจากผู้ใช้อย่างรวดเร็วเป็นหนึ่งในเรื่องที่ท้าทายที่สุดของประสิทธิภาพเว็บ ซึ่งก็คือทีม Chrome ทำงานอย่างหนักเพื่อช่วยให้นักพัฒนาเว็บตอบสนองความต้องการได้ ในปีนี้ เราได้ประกาศว่าเมตริก Interaction to Next Paint (INP) จะเปลี่ยนจากสถานะทดลองเป็นรอดำเนินการ โดยตอนนี้ฟีเจอร์ดังกล่าวจะแทนที่ First Input Delay (FID) เป็น Core Web Vitals ในเดือนมีนาคม 2024 แล้ว

ด้วยความพยายามอย่างต่อเนื่องที่จะส่งมอบ API ใหม่ๆ ที่จะช่วยให้นักพัฒนาเว็บสร้างเว็บไซต์ได้อย่างรวดเร็วเท่าที่จะเป็นไปได้ ขณะนี้ทีม Chrome ได้ใช้ช่วงทดลองใช้จากต้นทางสำหรับ scheduler.yield โดยเริ่มตั้งแต่ Chrome เวอร์ชัน 115 scheduler.yield เป็นส่วนเพิ่มใหม่ที่เสนอใน API เครื่องจัดตารางเวลาที่ให้ทั้งวิธีที่สะดวกและดีกว่าเดิมในการคืนการควบคุมกลับไปยังเทรดหลักมากกว่าวิธีการเดิมที่เคยใช้

เมื่อให้ผลตอบแทน

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

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

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

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

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

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

ปัญหาของกลยุทธ์ผลตอบแทนในปัจจุบัน

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

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

หากต้องการดูสถานการณ์จริง ให้ลองใช้การสาธิต Glitch นี้ หรือทดลองใช้ในเวอร์ชันที่ฝังอยู่ด้านล่าง การสาธิตประกอบด้วยปุ่ม 2-3 ปุ่มที่คุณสามารถคลิกได้ และช่องใต้ปุ่มนั้นซึ่งจะบันทึกเมื่องานถูกเรียกใช้งาน เมื่อมาที่หน้านี้แล้ว ให้ดำเนินการดังต่อไปนี้

  1. คลิกปุ่มด้านบนที่มีป้ายกำกับว่าเรียกใช้งานเป็นระยะ ซึ่งจะกำหนดเวลาให้งานการบล็อกทำงานบ่อยๆ เมื่อคลิกปุ่มนี้ บันทึกของงานจะแสดงข้อความต่างๆ ที่ระบุว่าเรียกใช้การบล็อกด้วย setInterval
  2. ถัดไป ให้คลิกปุ่ม เรียกใช้การวนซ้ำ ซึ่งให้ผลลัพธ์ setTimeout ในการทำซ้ำแต่ละครั้ง

โดยจะเห็นว่าช่องที่ด้านล่างของการสาธิตจะมีข้อความแบบนี้

Processing loop item 1
Processing loop item 2
Ran blocking task via setInterval
Processing loop item 3
Ran blocking task via setInterval
Processing loop item 4
Ran blocking task via setInterval
Processing loop item 5
Ran blocking task via setInterval
Ran blocking task via setInterval

เอาต์พุตนี้แสดงพฤติกรรม "สิ้นสุดคิวงาน" ที่เกิดขึ้นเมื่อส่งคืนด้วย setTimeout ลูปที่เรียกใช้การประมวลผล 5 รายการ และผลตอบแทนด้วย setTimeout หลังจากประมวลผลแต่ละรายการ

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

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

เข้าไปใน scheduler.yield

scheduler.yield พร้อมให้ใช้งานหลัง Flag ว่าเป็นฟีเจอร์แพลตฟอร์มบนเว็บเวอร์ชันทดลองมาตั้งแต่เวอร์ชัน 115 ของ Chrome คำถามหนึ่งที่คุณอาจมีอยู่คือ "ทำไมฉันถึงต้องใช้ฟังก์ชันพิเศษเพื่อตอบกลับในที่ setTimeout มีอยู่แล้ว"

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

scheduler.yield คือฟังก์ชันที่ให้ผลลัพธ์ไปยังเทรดหลักและแสดงผล Promise เมื่อเรียกใช้ ซึ่งหมายความว่าคุณจะawaitค่าในฟังก์ชัน async ได้ ดังนี้

async function yieldy () {
  // Do some work...
  // ...

  // Yield!
  await scheduler.yield();

  // Do some more work...
  // ...
}

หากต้องการดูการทำงานของ scheduler.yield ให้ทำดังนี้

  1. นำทางไปยัง chrome://flags
  2. เปิดใช้การทดสอบฟีเจอร์แพลตฟอร์มเว็บรุ่นทดลอง คุณอาจต้องรีสตาร์ท Chrome หลังจากดำเนินการนี้
  3. ไปที่หน้าสาธิตหรือใช้เวอร์ชันที่ฝังอยู่ด้านล่างรายการนี้
  4. คลิกปุ่มด้านบนที่มีป้ายกำกับว่าเรียกใช้งานเป็นระยะ
  5. สุดท้าย ให้คลิกปุ่ม "เรียกใช้ลูป" ซึ่งให้ผลลัพธ์กับ scheduler.yield ในการทำซ้ำแต่ละครั้ง

ผลลัพธ์ในช่องที่ด้านล่างของหน้าเว็บจะมีลักษณะดังนี้

Processing loop item 1
Processing loop item 2
Processing loop item 3
Processing loop item 4
Processing loop item 5
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval

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

ลองใช้วิธีนี้เลย

หากคุณสนใจ scheduler.yield และต้องการลองใช้ คุณสามารถทำได้ 2 วิธีโดยเริ่มจาก Chrome ในเวอร์ชัน 115 ดังนี้

  1. หากคุณต้องการทดลองใช้ scheduler.yield ในเครื่อง ให้พิมพ์และป้อน chrome://flags ในแถบที่อยู่ของ Chrome และเลือกเปิดใช้จากเมนูแบบเลื่อนลงในส่วนฟีเจอร์แพลตฟอร์มเว็บแบบทดลอง การดำเนินการนี้จะทำให้ scheduler.yield (และฟีเจอร์ทดลองอื่นๆ) ใช้ได้เฉพาะในอินสแตนซ์ของ Chrome เท่านั้น
  2. หากต้องการเปิดใช้ scheduler.yield สำหรับผู้ใช้ Chromium จริงในต้นทางที่เข้าถึงได้แบบสาธารณะ คุณจะต้องลงชื่อสมัครใช้ช่วงทดลองใช้ scheduler.yield จากต้นทาง ซึ่งช่วยให้คุณทดสอบฟีเจอร์ที่เสนอไว้ในระยะเวลาที่กำหนดได้อย่างปลอดภัยและให้ข้อมูลเชิงลึกที่เป็นประโยชน์แก่ทีม Chrome เกี่ยวกับวิธีการใช้ฟีเจอร์ดังกล่าวในการใช้งานจริง อ่านคู่มือนี้เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานของช่วงทดลองใช้จากต้นทาง

วิธีที่คุณใช้ scheduler.yield (ในขณะที่ยังรองรับเบราว์เซอร์ที่ไม่ได้ใช้) จะขึ้นอยู่กับเป้าหมายของคุณ คุณใช้ polyfill อย่างเป็นทางการได้ Polyfill จะมีประโยชน์ในกรณีต่อไปนี้

  1. คุณใช้ scheduler.postTask ในแอปพลิเคชันเพื่อตั้งเวลางานอยู่แล้ว
  2. คุณต้องการกำหนดลำดับความสำคัญของงานและผลตอบแทน
  3. คุณต้องการยกเลิกหรือจัดลำดับความสำคัญของงานผ่านคลาส TaskController ที่ scheduler.postTask API มีให้

หากไม่ตรงกับสถานการณ์ของคุณ การใช้ Polyfill ก็อาจไม่เหมาะกับคุณ ในกรณีดังกล่าว คุณสามารถเล่นวิดีโอสำรองของตัวเองได้ 2 วิธี วิธีการแรกจะใช้ scheduler.yield หากมี แต่จะใช้มูลค่ากลับเป็น setTimeout หากยังไม่มี

// A function for shimming scheduler.yield and setTimeout:
function yieldToMain () {
  // Use scheduler.yield if it exists:
  if ('scheduler' in window && 'yield' in scheduler) {
    return scheduler.yield();
  }

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

// Example usage:
async function doWork () {
  // Do some work:
  // ...

  await yieldToMain();

  // Do some other work:
  // ...
}

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

// A function for shimming scheduler.yield with no fallback:
function yieldToMain () {
  // Use scheduler.yield if it exists:
  if ('scheduler' in window && 'yield' in scheduler) {
    return scheduler.yield();
  }

  // Fall back to nothing:
  return;
}

// Example usage:
async function doWork () {
  // Do some work:
  // ...

  await yieldToMain();

  // Do some other work:
  // ...
}

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

รูปภาพหลักจาก Unsplash โดย Jonathan Allison