ป้ายกำกับ: Event Loop

JavaScript Event Loop & Event-driven Architecture: เจาะลึก Call Stack, Task Queue และ Microtask QueueJavaScript Event Loop & Event-driven Architecture: เจาะลึก Call Stack, Task Queue และ Microtask Queue

ในโลกของการพัฒนาซอฟต์แวร์สมัยใหม่ การที่โปรแกรมจะต้องสามารถทำงานหลายอย่างพร้อมกันได้อย่างราบรื่น โดยไม่เกิดอาการค้างหรือหยุดชะงัก เป็นสิ่งที่มีความสำคัญอย่างยิ่งยวด JavaScript ซึ่งเป็นภาษาที่ถูกออกแบบมาให้ทำงานบนสภาพแวดล้อมแบบ Single-threaded ทำให้การจัดการงานที่ต้องใช้เวลานาน เช่น การเรียก API ภายนอก หรือการจับเวลา (Timers) ต้องอาศัยกลไกพิเศษเพื่อไม่ให้ UI ของผู้ใช้งานเกิดอาการ “จม” (Blocking)

หัวใจสำคัญที่ทำให้ JavaScript สามารถจัดการงานเหล่านี้ได้อย่างมีประสิทธิภาพคือกลไกที่เรียกว่า Event Loop ซึ่งทำหน้าที่เป็นผู้ประสานงานหลักในการรับประกันว่าโค้ดจะถูกรันอย่างถูกต้องและมีลำดับความสำคัญที่ชัดเจน ระบบนี้ไม่ได้ทำงานด้วยการรันโค้ดตามลำดับเท่านั้น แต่เป็นการบริหารจัดการงานที่รอคิว (Queue Management) อย่างซับซ้อน

กลไกหลัก: Call Stack, Web APIs และ Task Queues

เพื่อให้เข้าใจการทำงานแบบ Non-blocking I/O ของ JavaScript เราต้องทำความเข้าใจส่วนประกอบหลัก 3 ส่วน ได้แก่:

  1. Call Stack: เป็นพื้นที่ที่เก็บการเรียกใช้ฟังก์ชันตามลำดับ (LIFO – Last In, First Out) เมื่อมีการเรียกใช้ฟังก์ชัน โค้ดจะถูกผลักเข้าสู่ Stack
  2. Web APIs: เป็นส่วนที่จัดการงานที่ต้องใช้เวลานาน เช่น `setTimeout()` หรือการเรียก Fetch API งานเหล่านี้จะถูกส่งไปให้ Web APIs ทำงานในเบื้องหลัง
  3. Task Queues (Microtask & Macrotask): เมื่อ Web APIs ทำงานเสร็จสิ้น งานเหล่านั้นจะไม่ถูกส่งกลับไปที่ Call Stack ทันที แต่จะถูกจัดคิวรอการประมวลผล

ลำดับความสำคัญของคิว (Queue Priority)

สิ่งที่นักพัฒนาต้องทำความเข้าใจอย่างลึกซึ้งคือลำดับการทำงานของคิว (Queue Priority) โดยเฉพาะความแตกต่างระหว่าง Microtask Queue และ Macrotask Queue:

  • Microtask Queue: ประกอบด้วยงานที่มีลำดับความสำคัญสูง เช่น การ `Promise.then()` หรือ `queueMicrotask` งานเหล่านี้จะถูกประมวลผลให้เสร็จสิ้นทั้งหมด ก่อน ที่ Event Loop จะไปดึงงานถัดไปจาก Macrotask Queue เสมอ
  • Macrotask Queue: ประกอบด้วยงานทั่วไป เช่น `setTimeout()` หรือ I/O event ต่างๆ งานเหล่านี้จะถูกประมวลผลทีละงานหลังจาก Microtasks ทั้งหมดถูกเคลียร์แล้ว

ในเชิงแนวคิดการจัดการทรัพยากรแบบไม่บล็อก (Non-blocking Resource Management) เราสามารถจำลองการทำงานของ Event Loop ได้โดยการกำหนดลำดับความสำคัญของงานที่ต้องรอการประมวลผล ดังตัวอย่างการจัดการคิวงานใน PostgreSQL:

SQL

-- สมมติว่าเรามีตารางงาน (task_queue) ที่มีลำดับความสำคัญ (priority) และเวลาที่ต้องรอ (scheduled_time)</code>
SELECT
    task_id,
    task_name,
    priority_level,
    scheduled_time
FROM
    task_queue
WHERE
    is_completed = FALSE
ORDER BY
    CASE WHEN priority_level = 'microtask' THEN 1
         WHEN priority_level = 'macrotask' THEN 2
         ELSE 3
    END,
    scheduled_time ASC
LIMIT 1;

จากโค้ดข้างต้น เราสามารถเห็นได้ว่าระบบจะถูกออกแบบให้ดึงงานที่มี `priority_level` เป็น ‘microtask’ ออกมาก่อนเสมอ ซึ่งสะท้อนถึงหลักการที่ Microtasks ต้องถูกเคลียร์ก่อน Macrotasks เสมอ เพื่อให้แน่ใจว่าการประมวลผลมีความต่อเนื่องและไม่เกิดการสะดุด (Stall).

บทสรุป

การทำความเข้าใจ Event Loop ไม่ใช่เพียงแค่การรู้คำศัพท์ทางเทคนิค แต่คือการเข้าใจถึงสถาปัตยกรรมที่ทำให้ JavaScript สามารถจัดการงานที่ซับซ้อนและต้องรอเวลาได้อย่างมีประสิทธิภาพ การแยกแยะความแตกต่างระหว่าง Microtask และ Macrotask Queue จึงเป็นทักษะสำคัญที่ช่วยให้นักพัฒนาสามารถควบคุมลำดับการทำงานของโค้ดได้อย่างแม่นยำ ทำให้แอปพลิเคชันทำงานได้อย่างรวดเร็วและตอบสนองต่อผู้ใช้ได้ตลอดเวลา