( บันทึกกันลืม ) การทำงานของ NodeJS

programming Jun 24, 2020

NodeJS

  • ใช้ Google V8 Engine ในการ compile Javascript ไปเป็น Byte code
  • เมื่อ code เรา ถูก compile จะถูกเก็บไว้ใน memory ต่อให้ลบ ไฟล์นั้นๆ ออกไป ก็ยังสามารถทำงานได้
  • มีการทำงาน แบบ Single-Thread ( 1 Thread : 1 Process ) ไม่ใช่ Multi-Thread แต่เพราะ การทำงานที่เป็น Non-Blocking I/O , Event Loop ของมันทำให้เข้าใจว่าเป็นการทำงานแบบ Muti-Thread Processing

Capture6
รูปจาก https://ima8.me/2015/01/12/node-js-say-hi-what-is-it/

Blocking I/O

เป็นการทำงานที่เกิดการรอเกิดขึ้น ไม่ว่าจะมีการทำงานพร้อมกันหลายๆงาน ยังไงงานก็ถูกกองไปอยู่ที่เดียว รองานเก่า จะทำเสร็จและตอบกลับมา

  • ข้อดีของ Blocking I/O
    • เข้าใจง่าย
    • มีการทำงานที่เป็น ลำดับ
  • ข้อเสีย Blocking I/O
    • สิ้นเปลืองทรัพยากร เพราะอาจจะมี Thread ตัวอื่นที่ถูกสร้างไว้ แต่ไม่ถูกใช้งานจำนวนมาก
      threading_java-1

รูปจาก https://webapplog.com/you-dont-know-node/threading_java/


Non-Blocking I/O

เป็นการทำงานอะไร สักอย่างโดนที่ไม่ต้องรอ การทำงานของงานก่อนหน้าทำเสร็จ ต่อให้มีการทำงานหลายๆงานพร้อมกัน ก็จะมีการแบ่งงานของ Thread เกิดขึ้น ไม่ได้ไปกองอยู่ที่ๆเดียวกัน

  • ข้อดีของ Non-Blocking I/O
    • เร็ว
    • ไม่เกิดการรอ การทำงานของงานที่ถูกสั่งมา
  • ข้อเสีย Non-Blocking I/O
    • มีการทำงานที่ไม่เป็นไป ตามลำดับ

threading_node--1-


Event Loop Model

เป็นการโมเดลการ ทำงานแบบ Asynchronous จะมีขั้นตอนประมาณนี้

- เมื่อมี Request เข้ามา ระบบ จะไปหยิบ Thread มารับงานที่เราสั่งมา
- จากนั้น Thread จะนับงานไปทำ ``อะไรสักอย่างที่เราสั่ง`` ไปทำงาน
- จากนั้น Thread นั้นจะไปรับงานถัดไป โดยที่ไม่รอ ``งาน`` เดิมเสร็จ 

4fJ5XSy-1


*** ก่อนจะไปเรื่องการทำงาน Async/Await จะมีการทำให้ Function ทำงาน แบบ Asynchronous อยู่อีก 2 แบบ คือ

  • Callback Function

เป็นการ เขียน Function ซ้อน Function เพื่อรอการทำงาน จะเป็นประมาณนี้

function A(param ,function(err,resA){
    function B(resA,function(err,resB)){
        // ทำไรสักอย่าง
    })
)}

โดยปกติ จะให้เป็น parameter ตัวแรก เป็น error แล้วเช็คว่า ถ้า error เป็น null แสดงว่าไม่ error

  • Promise

เกิดขึ้นจาก การทำ callback function ที่มีความซับซ้อนมากๆ ( callback hell )
ประกอบไปด้วย 3 สถานะ
- Pending
เป็นสถานะแรกสุดเมื่อมีการเริ่มเรียก
- Resolved
เป็นสถานะที่ได้หลังจากการเรียกที่ไม่ error และจะอยู่ใน .then()
- Rejected
เป็นสถานะที่ถูกตอบออกมาหลังจากเรียกแล้วมี error และจะอยู่ใน .catch()

จะเป็นประมาณนี้

const datas = new Promise((resolved,reject)=> {// ทำอะไรสักอย่าง} )
.then(data=> // {resolved})
.catch(err=> // {rejected})

การทำงาน Async/Await

ด้วยความที่ Node มีการทำงานแบบ Non-Blocking I/O หรือ Asynchronous แต่การใช้งาน Function บางอย่างอาจจะเป็น Synchronous เช่น การทำอะไรกับฐานข้อมูล หรือทำอะไรที่ต้องรอให้คำสั่งนั้นทำงานเสร็จถึงจะไปทำอันถัดไป
จึงได้มีการนำ การ Async/Await มาทำในส่วนนี้

  • ตัวอย่าง การใช้ Axios
let datas = axios.get('xxx.com')

console.log(datas)

สิ่งที่ axios ส่งออกมาคือค่า Promise Object เพราะ ภายใน axios เป็นการทำงาน แบบ Asynchronous ซึ่งสามารถรอรับค่าจากที่ axios ส่งมาได้อีกทางด้วยการ ใน callback

axios.get('xxx.com').then(data=>console.log(data))
  • ก็จะเป็นการรอรับ ค่าที่ axios ตอบกลับมาได้

แล้วถ้าในกรณี ที่มีการ เลือกให้ Axios หลายที่ ก็จะต้องทำประมาณนี้

//งาน A
axios.get('xxx.com').then(xdata=>console.log(xdata))
//งาน B
axios.get('yyy.com').then(ydata=>console.log(ydata))

ที่นี้ แล้วถ้าเป็นในกรณี ที่เราต้องการที่จะให้ งาน A ทำงานเสร็จก่อนแล้วจึงไปทำงาน B ละ
ก็จะได้ประมาณนี้

// งาน A
axios.get('xxx.com').then(xdata=>{
    // งาน B
    axios.get('yyy.com').then(ydata=>{
        console.log(ydata)
    })
})

ที่นี้ การทำงานของเราก็จะเป็นทำงานแบบเรียงตามลำดับได้แล้ว
แต่แลกมากับปัญหา callback hell ลองนึกสภาพที่ มีการทำคำสั่งแบบนี้ สัก 10 ขั้นละ
นอกจากมั่วแล้ว ยังอ่านยาก อีก จึงนำมาซึ่ง Async/Await

  • Async/Await

    เกิดจากการข้อเสีย ที่ Promise พอลำดับเริ่มเยอะขึ้น มีการใช้ .then() ไปหลายชั้น คล้าย callback hell แต่ในที่นี้เรียกว่า promise hell

การใช้งาน Async/Await

  • กำหนด Async ไว้หน้า function เพื่อบอกว่า function นี้มีการทำงานแบบ Asynchronous
  • ใช้ Await เพื่อบอกให้รอการทำงานจาก function นี้ก่อน แล้วจึงไปทำ function ถัดไป
    เช่น
// แบบปกติ
function A(){
    console.log("A")
}

// แบบ async function
async function A(){
    console.log("A")
}

> ที่นี้ เรียกใช้ function ด้วย Await

async function main (){
    let datas = await A();
    console.log(data) // A
}
> ยกตัวอย่างจาก axios ด้านบน 
// แบบปกติ
axios.get('xxx.com').then(xdata=>{
    // งาน B
    axios.get('yyy.com').then(ydata=>{
        console.log(ydata)
    })
})

/// แบบ async function 
async function call(){
    let callA = await axios.get('xxx.com')
    let callB = await axios.get('yyy.com')
}

เพียงเท่านี้ ก็จะสามารถ ทำงานเป็นลำดับ และอ่านง่ายขึ้นด้วย

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.