NodeJS Stream ไหลดั่งสายน้ำ ตอนที่ 1 (Readable)

15 March, 2019, By Tar Jarupong

NodeJS stream นี้มันอะไรกันนะ มันสำคัญขนาดนั้นเลยเหรอ ไม่เห็นเคยได้ยิน ใช่ผมก็ไม่เคยได้ยินแต่ด้วยต่อมอยากเ-อก เอ้ยอยากรู้มันทำงานก็เลยได้ไปศึกษามาแล้วอยากจะเขียนบทความให้ทุกคนได้เข้าใจกันครับ มาเริ่มกันเลยดีกว่า

อะไรคือ Stream

มันคือ data flow ที่ไหลไปเรื่อยๆๆ ผู้อ่านทุกคน คงจะงงว่า "ไหลไปเรื่อยๆ" เป็นยังไง เดี๋ยวผมจะวาดภาพให้ดูรอสักแป็บนึงครับ

ถ้าดูจากรูปจะเห็นว่าถ้าเราต้องการที่จะเขียนหรืออ่านไฟล์อะไรเราจะต้องใช้ function หลัก 2 ตัวนี้แน่นอน

fs.readFile()

fs.writeFile()

แต่การทำแบบนี้มันผิดมากๆครับ ก็เพราะว่ามันคือรูปข้างบนที่ผมวาดไว้(รูปแรก) ทุกคนจะเห็นว่าวิธีการของมันก็คือเอาข้อมูลที่เราต้องเขียน อ่าน มาเก็บลง memory ตรงๆเลยแล้วถ้าสมมุติว่าขนาดไฟล์มีขนาด 4 GB บางคนอาจจะคิดว่า "โอ้ยสบาย server ผมน่ะมี RAM ตั้ง 128 GB แค่นี้สบายมากๆ" แต่ถ้าสมมุติว่ามีคนอัพโหลดไฟล์พร้อมกันประมาณ 100 คนและขนาด 4 GB เหมือนกันจะจัดการยังไงล่ะ คงไม่ไหวแน่นอนครับ ตรงจุดนี้ถึงเป็นเหตุสมควรที่จะใช้ Streams แล้วล่ะครับ



ข้อดี Stream

      1. ทำให้ใช้ memory ได้น้อยลงเพราะตัว Streams จะทำการค่อยๆ process ทีละนิดแล้วคอยเช็คว่าข้อมูลที่ต้องการ process ยังมีอยู่มั๊ย ถ้ายังมีอยู่ก็ process ต่อไป
      2. ทำให้ใช้เวลาเริ่มต้นการทำงานน้อยลงเพราะว่ามันไม่ต้องเสียเวลา process ก้อนข้อมูลอันใหญ่ มันก็แค่ค่อยทำทีละหน่อยๆ



หลักๆมี 4 แต่วันขอพูดแค่ 1

       1.Readable คือการอ่านข้อมูล จะเสียเวลาอยู่ทำไมกันมาดูตัวอย่างกันเลยดีกว่าครับ เดี๊ยว!!! ผมลืมอธิบายราละเอียดของ event และ function ต่างๆเลย ผมขออธิบายตรงนี้ก่อนนะครับ

      event จะมีหลักๆก็จะมี

      'data' เป็น event สำหรับคอยตรวจสอบเวลาที่มีข้อมูลเข้ามาใหม่

      'end' เป็น event ที่จะบอกว่าไม่มีข้อมูลใหม่มาแล้ว

      'error' เป็น event ที่คอยบอกว่าเกิด error อะไรขึ้นบ้างระหว่างทาง

      'close' เป็น event ที่เกิดขึ้นตอนที่มีการเรียกใช้ destroy();

       function ก็จะมี

       on() สำหรับเรียกใช้เพื่อที่จะตรวจสอบด้วย event ต่างๆ

       push() สำหรับส่งข้อมูลเข้าไป

       pause() หยุดการทำงานชั่วคราว

       resume() สั่งให้ stream มีการทำงานต่อได้

       isPaused() เป็นตัวเอาไว้ตรวจสอบว่า stream ตอนนี้มีการหยุดรึเปล่า

       destroy() เป็นการ

เฮ้อในที่สุดผมก็เขียนเสร็จสักที เสียเวลาอยู่ทำไมมาเริ่มตัวอย่างกันเลย~~~



ตัวอย่างที่ 1


const { Readable } = require('stream')

const readStream = new Readable({
  read(){}
})

readStream.on('data',(data)=>{
  console.log(data)
})

readStream.on('end',()=>{
  console.log("No more data")
})

readStream.push("Hello world");
readStream.push("Hello Krapoke Node");

readStream.push(null);

ตรงนี้ผมได้ทำการสร้าง listener 2 ตัวโดยอันแรกสำหรับตรวจสอบว่ามีข้อมูลเข้ามาหรือไม่ และอันที่สองสำหรับตรวจสอบว่าจบการทำงานรึยัง ถัดมาทุกคนจะเห็นว่าผมได้ทำการส่งคำว่า "Hello world" กับ "Hello krapoke Node" เข้าไปใน stream และจบสุดท้ายด้วย null ตรงนี้ผมใส่ไปเพื่อที่จะบอกว่าจบสำหรับการ ก็จะได้ผลลัพธ์ตามนี้เลยครับ

ทุกคนจะเห็นว่า จะส่งมาเป็น buffer ครับ แล้วถ้าเราต้องการจะแปลงเป็น string ก็แค่เปลี่ยน console.log(data) => console.log(data.toString) แค่นี้ก็ได้แล้วครับ

ทั้งหมดคือตัวอย่างแรกที่พูดถึงพื้นฐานของ read data stream ครับ เรามาดูต่อว่าการใช้ pause(), resume() เป็นยังไงกันนนนน



_ตัวอย่างที่ 2

const { Readable } = require('stream')

const readStream = new Readable({
  read(){}
})

readStream.on('data',(data)=>{
  console.log("data arrive => ",data.toString())
})

readStream.on('end',()=>{
  console.log("No more data")
})


readStream.push("Hello world");

setTimeout(()=>{
  console.log("Pause Stream");
  readStream.pause();
},500)

setTimeout(()=>{
  console.log("New data arrived");
  readStream.push("Hello Krapoke Node");
},1000)

ตรงนี้โค๊ดอาจจะงงนิดนึงครับ อธิบายคร่าวๆคือ ผมอ่านข้อมูล "Hello world" แล้วหลังจากนั้นเวลาผ่านไป 0.5 วินาทีให้ทำการหยุด stream ชั่วคราวดังนั้นทุกคนจะเห็นว่าอีก 0.5(1 วินาทีตั้งแต่เริ่มรัน) วินาที มีข้อมูลใหม่คือคำว่า "Hello Krapoke Node" ผลลัพธ์จะได้ตามนี้ครับ

จะเห็นว่าไม่มี "Hello Krapoke Node" ออกมาเลยเพราะว่าผมได้ทำการหยุด stream ไว้ เลยทำให้ตัว stream ไม่ได้รับข้อมูลเข้ามาครับ

โอเคจบกันไปกับ readable stream ครับ


Ref:

doc

node stream