ข้ามไปยังเนื้อหา

Streams & I/O

ลองนึกภาพการอ่านไฟล์ Log ขนาด 4 GB ถ้าคุณโหลดข้อมูลทั้งหมดเข้า Memory ก่อน Node จะต้องจองพื้นที่ 4 GB ก่อนที่จะอ่านบรรทัดแรกได้ด้วยซ้ำ หากเครื่องมี RAM แค่ 2 GB กระบวนการจะพังก่อนที่จะเริ่มต้น

Stream แก้ปัญหานี้ด้วยการประมวลผลข้อมูล ทีละ chunk — คุณเริ่มอ่าน chunk แรกได้เลยขณะที่ chunk ที่เหลือยังมาไม่ถึง ทำให้ Memory ใช้คงที่ไม่ว่าแหล่งข้อมูลจะใหญ่แค่ไหน

แนวคิดเดียวกันนี้ใช้กับการเขียนด้วย แทนที่จะ Buffer Response ทั้งหมดใน Memory แล้วส่งในครั้งเดียว Stream จะส่ง chunk แต่ละชิ้นไปยังปลายทางทันทีที่ข้อมูลมาถึง

Node.js กำหนดประเภท Stream นามธรรมสี่ชนิดใน module node:stream:

ประเภททิศทางตัวอย่าง
Readableข้อมูลไหล ออกfs.createReadStream, process.stdin
Writableข้อมูลไหล เข้าfs.createWriteStream, process.stdout
DuplexสองทิศทางTCP socket (net.Socket)
Transformสองทิศทางพร้อมแปลงข้อมูลzlib.createGzip (บีบอัดระหว่างทาง)

ทุก Stream ยังสืบทอดจาก EventEmitter ด้วย คลาสพื้นฐานของ Stream emit event มาตรฐาน (data, end, error, drain, finish) ที่คุณแนบ handler ได้

บทเรียนหัวข้อ
Readable Streamsevents data/end/error, Readable.from, async iteration
Writable Streamswrite(), end(), และ event drain
Pipe & Pipelineเชื่อม Stream ด้วย .pipe() และ stream.pipeline()
Backpressureคืออะไร สำคัญอย่างไร และ Pipeline จัดการอย่างไร

Demo ด้านล่างใช้ Readable.from สร้าง Readable Stream จาก Array ของ String แล้วอ่านทีละ chunk ด้วย for await สังเกตว่าไม่มีการเก็บ Array ทั้งหมดไว้ใน Buffer — แต่ละรายการถูก yield เมื่อต้องการเท่านั้น

Node.js

Needs the Node.js runtime — open in StackBlitz to run.

ทำไม Stream จึงดีกว่าการโหลดไฟล์ทั้งหมดเข้า Memory?
Stream ชนิดใดใน Node.js ที่แปลงข้อมูลระหว่างทาง?
คลาสพื้นฐานที่ Stream ทั้งสี่ประเภทสืบทอดมาคืออะไร?