เจาะลึกคำถามสัมภาษณ์ Node.js: ทำไมต้องเข้าใจ Event Loop และการทำงานเบื้องหลัง

8 นาที 20 views บันทึกเป็น PDF
เจาะลึกคำถามสัมภาษณ์ Node.js: ทำไมต้องเข้าใจ Event Loop และการทำงานเบื้องหลัง

เตรียมตัวสัมภาษณ์งาน Node.js ให้ผ่านฉลุย! มาทำความเข้าใจว่าทำไม Event Loop, Microtasks และ Streams ถึงเป็นหัวใจสำคัญที่บริษัทชั้นนำใช้คัดเลือกโปรแกรมเมอร์

ทำไมคำถามสัมภาษณ์ Node.js ถึงวนอยู่ที่เรื่องเดิม

เวลาไปสัมภาษณ์งานตำแหน่ง Node.js คุณมักจะเจอคำถามที่ดูเหมือนไม่เกี่ยวกัน เช่น เรื่องการจัดการไฟล์, การตั้งเวลา หรือการรับส่งข้อมูลจำนวนมาก หลายคนพยายามจำคำตอบแยกกันทีละเรื่อง แต่ความจริงแล้วทุกเรื่องมีรากฐานมาจากจุดเดียว คือคุณต้องเข้าใจว่า Node.js ทำงานแบบ Single-thread (มีพนักงานทำงานแค่คนเดียว) และคุณต้องรู้ว่าถ้าพนักงานคนนี้โดนขวางงานขึ้นมา จะเกิดอะไรขึ้น

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

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

Event Loop: หัวใจของการทำงานที่ไม่หยุดรอ

หลายคนนิยามว่า Node.js คือการทำงานแบบ Non-blocking (ไม่หยุดรอให้งานหนึ่งเสร็จก่อนค่อยทำอย่างอื่น) และ Asynchronous (การทำงานแบบไม่ต้องรอผลลัพธ์ทันที) แต่คำนิยามนี้ยังไม่พอ สิ่งที่สำคัญกว่าคือการรู้ว่างานแต่ละประเภทวิ่งไปที่ไหน งานที่เกี่ยวกับ I/O (การรับส่งข้อมูลเข้าออก เช่น อ่านไฟล์หรือเรียกฐานข้อมูล) จะถูกโยนออกไปให้ระบบจัดการ แล้ว Node.js จะกลับมาทำงานต่อทันที

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

จุดที่มือใหม่มักพลาดคือการคิดว่า Node.js จะแบ่งงานให้โดยอัตโนมัติ แต่ความจริงคือคุณต้องเป็นคนคุมเอง ถ้าคุณมีงานคำนวณหนักๆ คุณต้องแยกออกไปที่ Worker thread (การแยกงานไปทำในคนละสายงาน) เพื่อไม่ให้กระทบผู้ใช้งานคนอื่น การตอบแบบนี้จะแสดงให้เห็นว่าคุณเข้าใจข้อจำกัดของเครื่องมือที่ใช้จริงๆ

ลำดับการทำงานและกับดักของ Microtasks

คำถามยอดฮิตคือ "อะไรจะรันก่อนกัน" ระหว่าง Promise (คำสัญญาว่าจะให้ผลลัพธ์ในอนาคต) กับ Timer (การตั้งเวลาให้ทำงาน) คำตอบไม่ใช่แค่เรื่องลำดับ แต่เป็นเรื่องของ Queue (คิวหรือแถวรอ) ที่แตกต่างกัน ระหว่าง Microtasks (งานด่วนที่ต้องทำทันทีหลังจบงานหลัก) และ Macrotasks (งานทั่วไปที่รอคิวได้)

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

// ตัวอย่างการค้างของระบบจาก Microtasks
function loop() {
  Promise.resolve().then(loop); // ฟังก์ชันนี้จะแย่งคิวงานจนระบบค้าง
}
// หากรันโค้ดนี้ ระบบจะไม่สามารถทำอย่างอื่นได้อีกเลย

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

Streams และความลับของ Backpressure

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

คำตอบคือ Backpressure (กลไกการบอกให้หยุดส่งข้อมูลเมื่อรับไม่ไหว) ถ้าไม่มีสิ่งนี้ ข้อมูลจะถูกเก็บสะสมในหน่วยความจำจนหมดและระบบจะพัง วิธีแก้ที่ดีที่สุดคือใช้ pipeline เพื่อให้ระบบจัดการหยุดส่งข้อมูลให้โดยอัตโนมัติเมื่อฝั่งรับส่งสัญญาณว่า "เต็มแล้ว"

// การใช้ pipeline เพื่อจัดการ Backpressure ให้ปลอดภัย
const { pipeline } = require('stream/promises');

async function run() {
  // pipeline จะหยุดอ่านไฟล์ถ้าฝั่งเขียนข้อมูลเขียนไม่ทัน
  await pipeline(source, transform, res);
}

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

วิธีรับมือกับ Error ในโลก Asynchronous

ใน Node.js การทำ Error handling (การจัดการเมื่อเกิดข้อผิดพลาด) พลาดเพียงนิดเดียวอาจทำให้ระบบเงียบหายไปเลย เช่น การลืม await หน้าฟังก์ชันที่มี Promise หรือการโยน Error (ข้อผิดพลาด) ทิ้งไว้ใน Callback (ฟังก์ชันที่เรียกตามหลัง) โดยไม่มีคนรับ ระบบจะไม่ออกอาการทันที แต่จะค้างในสถานะที่ไม่รู้จัก

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

มือใหม่มักจะพยายามเขียน try-catch ครอบทุกที่ แต่ถ้าใช้กับงาน Async (งานที่ไม่รอผลลัพธ์) แบบผิดวิธี มันจะจับไม่ได้เลย การเน้นว่าคุณจะจัดการอย่างไรเมื่อระบบพัง คือการพิสูจน์ว่าคุณพร้อมดูแลระบบที่ต้องเปิดใช้งานตลอด 24 ชั่วโมง

สรุป: การมองภาพรวมของระบบ

สิ่งที่คุณต้องจำให้ขึ้นใจคือ "ทุกอย่างใน Node.js หมุนรอบเรื่องเดียว คือการจัดการ 1 Thread (สายงานหลัก) ให้มีประสิทธิภาพที่สุด" ไม่ว่าจะเป็นเรื่อง Memory leak (หน่วยความจำรั่วไหล), การทำ Clustering (การเพิ่มจำนวนโปรเซสเพื่อรองรับงาน), หรือเรื่องอื่นๆ ทั้งหมดคือการบริหารจัดการทรัพยากรที่มีจำกัด

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

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


ที่มา: Node.js interview deep dive: what they are really testing — DEV Community

แชร์บทความ

Facebook X LINE

บทความที่เกี่ยวข้อง

เทคนิคเขียนแอป Flutter สำหรับ Meta Smart Glasses ให้ลื่นไหลและมีประสิทธิภาพ

เทคนิคเขียนแอป Flutter สำหรับ Meta Smart Glasses ให้ลื่นไหลและมีประสิทธิภาพ

เรียนรู้วิธีเขียนแอป Flutter เชื่อมต่อ Meta Smart Glasses ให้ทำงานเร็ว ไม่กระตุก ด้วยการวางสถาปัตยกรรมโค้ดและการจัดการข้อมูลแบบมือโปรที่มือใหม่ทำตามได้จริง

ที่มา: DEV Community

6 hours ago 10 นาที
5 views
เปรียบเทียบ WebSocket, SSE และ Polling เลือกวิธีทำระบบ Real-Time ให้เหมาะกับงาน

เปรียบเทียบ WebSocket, SSE และ Polling เลือกวิธีทำระบบ Real-Time ให้เหมาะกับงาน

อยากทำระบบ Real-Time แต่ไม่รู้จะเลือกใช้ Polling, SSE หรือ WebSocket ดี? มาดูวิธีเลือกใช้ให้เหมาะกับงาน เพื่อให้แอปของคุณทำงานลื่นไหลและประหยัดทรัพยากรเซิร์ฟเวอร์

ที่มา: DEV Community

9 hours ago 10 นาที
4 views