- เช่นเดียวกับ Progressive JPEG สามารถส่งข้อมูล JSON ในสภาพที่ยังไม่สมบูรณ์ก่อน เพื่อให้ไคลเอนต์ค่อย ๆ นำเนื้อหาทั้งหมดไปใช้งานได้ทีละส่วน
- วิธีพาร์ส JSON แบบเดิม มีปัญหาด้านประสิทธิภาพ เพราะไม่สามารถทำอะไรได้เลยจนกว่าจะรับข้อมูลครบทั้งหมด
- ใช้วิธีแบบ Breadth-first แบ่งข้อมูลออกเป็นหลายชังก์ (ส่วนย่อย) โดยส่วนที่ยังไม่พร้อมจะแทนด้วย Promise และค่อย ๆ เติมเมื่อพร้อม ทำให้ไคลเอนต์ใช้งานข้อมูลที่ยังไม่สมบูรณ์ได้
- แนวคิดนี้คือแกนสำคัญของ React Server Components(RSC) และใช้
<Suspense> เพื่อควบคุมสถานะการโหลดแบบเป็นขั้นตามที่ตั้งใจไว้
- สามารถแยก data streaming ออกจาก flow การโหลด UI ที่ตั้งใจออกแบบไว้ เพื่อมอบประสบการณ์ผู้ใช้ที่ยืดหยุ่นยิ่งขึ้น
แนวคิดของ Progressive JPEG และ Progressive JSON
- Progressive JPEG ไม่ได้โหลดภาพจากบนลงล่างทีเดียว แต่จะแสดงภาพรวมแบบเบลอก่อน แล้วค่อย ๆ ชัดขึ้น
- ในทำนองเดียวกัน หากนำแนวทางแบบ progressive มาใช้กับการส่ง JSON ก็จะ ใช้งานข้อมูลบางส่วนได้ทันทีโดยไม่ต้องรอให้ทั้งหมดเสร็จสมบูรณ์
- ในโครงสร้างข้อมูล JSON ตัวอย่าง วิธีปกติจะพาร์สได้ก็ต่อเมื่อรับข้อมูลครบจนถึงไบต์สุดท้ายเท่านั้น
- ด้วยเหตุนี้ ไคลเอนต์จึงต้อง รอจนกว่าทุกอย่างจะถูกส่งมาครบ แม้กระทั่งส่วนที่ช้าของเซิร์ฟเวอร์ (เช่น การโหลด comments จาก DB ที่ช้า) ซึ่งเป็นมาตรฐานปัจจุบันที่ไม่มีประสิทธิภาพมาก
ข้อจำกัดของ streaming JSON parser
- หากนำ streaming JSON parser มาใช้ ก็สามารถสร้าง ต้นไม้อ็อบเจ็กต์ข้อมูลที่ยังไม่สมบูรณ์ (ระหว่างทาง) ได้
- แต่เมื่อฟิลด์ของแต่ละอ็อบเจ็กต์ (เช่น footer, รายการ comment หลายรายการ ฯลฯ) ถูกส่งมาเพียงบางส่วน จะเกิดปัญหา ชนิดข้อมูลไม่สอดคล้อง และยากต่อการรู้ว่าสมบูรณ์หรือยัง จึงนำไปใช้ได้ยาก
- คล้ายกับการเรนเดอร์ HTML แบบสตรีมมิง หากประมวลผลสตรีมตามลำดับ ก็จะมีปัญหาเดียวกันคือ ส่วนที่ช้าเพียงจุดเดียวทำให้ผลลัพธ์ทั้งหมดล่าช้า
- นี่จึงเป็นเหตุผลที่โดยทั่วไปไม่ค่อยมีการใช้งาน streaming JSON
ข้อเสนอเรื่องโครงสร้าง Progressive JSON
- แทนที่จะใช้การสตรีมแบบ depth-first ตามเดิม (คือส่งโดยไล่ลึกลงไปในโครงสร้างต้นไม้ก่อน) จึงเสนอการใช้วิธี Breadth-first
- ส่งเฉพาะอ็อบเจ็กต์ระดับบนสุดมาก่อน แล้วปล่อยค่าด้านล่างไว้เป็น placeholder คล้าย Promise ก่อน จากนั้นค่อยเติมแต่ละส่วนเป็นชังก์เมื่อพร้อม
- ตัวอย่างเช่น ทุกครั้งที่เซิร์ฟเวอร์โหลดข้อมูลแบบอะซิงโครนัสเสร็จ ก็ส่งชังก์ที่เกี่ยวข้องมา และไคลเอนต์ก็ใช้งานได้เท่าที่พร้อมแล้ว
- ทำให้เกิด การรับข้อมูลแบบอะซิงโครนัส (โหลดได้ตั้งแต่เนิ่น ๆ) และไม่ต้องรอทั้งระบบจนกว่าส่วนที่ช้าหลายส่วนจะประมวลผลเสร็จทั้งหมด
- หาก ออกแบบไคลเอนต์ให้รองรับการรับชังก์แบบไม่เรียงลำดับและแบบเรียงลำดับบางส่วนได้ดี เซิร์ฟเวอร์ก็จะใช้กลยุทธ์การแบ่งชังก์ได้อย่างยืดหยุ่นมากขึ้น
Inlining และ Outlining: การส่งข้อมูลอย่างมีประสิทธิภาพ
- ฟอร์แมตการสตรีม Progressive JSON ยังสามารถจัดการอ็อบเจ็กต์ที่ใช้ซ้ำ (เช่น การอ้างอิง userInfo เดียวกันจากหลายตำแหน่ง) ได้โดยไม่ต้องเก็บซ้ำ ด้วยการ แยกออกมาเป็นชังก์เดียวต่างหาก แล้วอ้างอิงร่วมกันจากแต่ละตำแหน่งได้
- แยกเฉพาะส่วนที่ช้าออกมาเป็น placeholder แล้วส่งส่วนที่เหลือที่พร้อมก่อน เพื่อสร้าง data stream ที่มีประสิทธิภาพ
- หากอ็อบเจ็กต์เดียวกันปรากฏหลายครั้ง ก็สามารถ ส่งเพียงครั้งเดียวแล้วนำกลับมาใช้ซ้ำ (Outlining) ได้
- ด้วยวิธีนี้ แม้แต่ circular reference (โครงสร้างที่อ็อบเจ็กต์อ้างอิงตัวเอง) ก็ไม่เป็นปัญหาแบบ JSON ปกติ และสามารถ serialize ได้อย่างเป็นธรรมชาติผ่านโครงสร้างการอ้างอิงทางอ้อมระหว่างชังก์
การทำ progressive streaming ของ React Server Components(RSC)
- React Server Components คือหนึ่งในตัวอย่างเด่นของการนำโมเดล progressive JSON streaming ไปใช้จริง
- เซิร์ฟเวอร์ใช้โครงสร้างที่โหลดข้อมูลภายนอก (เช่น Post, Comments) แบบอะซิงโครนัส
- ฝั่งไคลเอนต์จะ เก็บส่วนที่ยังมาไม่ถึงไว้เป็น Promise และค่อย ๆ เรนเดอร์ UI ตามลำดับที่ข้อมูลพร้อม
- ใช้ React
<Suspense> เพื่อกำหนดสถานะการโหลดตามที่ตั้งใจไว้
- เพื่อ ป้องกันการกระโดดของหน้าจอที่ไม่จำเป็นในแง่ประสบการณ์ผู้ใช้ จึงไม่แสดงสถานะ Promise (ช่องโหว่) ทันที แต่ใช้ fallback ของ
<Suspense> เพื่อสร้างลำดับการโหลดเป็นขั้น ๆ
- แม้ข้อมูลจะมาถึงอย่างรวดเร็ว นักพัฒนาก็ยังควบคุมให้ UI ค่อย ๆ แสดงตามลำดับที่ออกแบบไว้ได้
สรุปและนัยสำคัญ
- นวัตกรรมสำคัญของ React Server Components คือการ สตรีม props ของ component tree แบบค่อยเป็นค่อยไปจากด้านนอกเข้าไป
- ดังนั้นจึงไม่จำเป็นต้องรอให้เซิร์ฟเวอร์เตรียมข้อมูลทุกอย่างเสร็จก่อน แต่สามารถแสดงส่วนสำคัญก่อนทีละน้อย พร้อมควบคุมสถานะรอโหลดได้อย่างละเอียด
- ไม่ใช่แค่ตัวสตรีมมิงเท่านั้น แต่ยังต้องมีโครงสร้างสนับสนุนเชิงโมเดลการเขียนโปรแกรม (เช่น React
<Suspense>) เพื่อใช้ประโยชน์จากมันด้วย
- ด้วยแนวทางนี้ จึงช่วยบรรเทาคอขวดของวิธีส่งข้อมูลแบบเดิม เช่น ปัญหาที่ข้อมูลช้าเพียงส่วนเดียวทำให้ทั้งระบบล่าช้า
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News