ลองดูโค้ดนี้แล้วทายว่าจะแสดงผลออกมาในลำดับไหน ถ้าคุณตอบว่า A B C บทความนี้เขียนมาเพื่อคุณครับ
setTimeout(() => console.log("A"), 0);
Promise.resolve().then(() => console.log("B"));
console.log("C");คำตอบที่ถูกคือ C B A ครับ ถึงจะตั้ง setTimeout ไว้ 0 มิลลิวินาที มันก็ยังมาทีหลังสุด เหตุผลอยู่ที่สิ่งที่เรียกว่า Event Loop
JavaScript ทำได้ทีละอย่าง
JavaScript ในเบราว์เซอร์ทำงานบน thread เดียว แปลว่าในแต่ละช่วงเวลารันโค้ดได้แค่ชุดเดียว งานที่ต้องรอ เช่น ตั้งเวลา โหลดข้อมูล หรือรอผู้ใช้คลิก จึงถูกส่งออกไปให้เบราว์เซอร์จัดการ แล้วค่อยกลับเข้ามาเมื่อพร้อม
คำถามคือ “กลับเข้ามาตอนไหน และเรียงลำดับยังไง” ซึ่งคำตอบมีอยู่ 3 ส่วน
- Call Stack — โค้ดที่กำลังรันอยู่ตอนนี้
- Microtask Queue — คิวของ
Promise.then,queueMicrotask,await - Macrotask Queue (Task Queue) — คิวของ
setTimeout,setInterval, event ต่างๆ
กฎของ Event Loop
Event Loop ทำงานวนซ้ำตามกฎง่ายๆ แค่นี้
- รันโค้ดใน Call Stack จนว่าง
- รัน Microtask ทั้งหมด ที่อยู่ในคิว (ถ้ามีงานใหม่เพิ่มเข้ามา ก็รันต่อจนหมด)
- หยิบ Macrotask มา 1 งาน แล้วกลับไปข้อ 1
ดูทีละขั้น
กด “ขั้นต่อไป” เพื่อดูว่าโค้ดตัวอย่างด้านบนเดินทางผ่านแต่ละคิวยังไง
Call Stack
Microtask
Macrotask
แล้ว async/await ล่ะ?
await เป็นแค่ Promise ในอีกรูปแบบหนึ่ง โค้ดที่อยู่ หลัง await จะถูกส่งเข้า Microtask Queue เหมือน .then()
async function run() {
console.log("1");
await null;
console.log("3"); // ← เข้า microtask
}
run();
console.log("2");ลองรันเอง
แก้โค้ดด้านล่างแล้วกด Run เพื่อดูลำดับจริง โค้ดรันอยู่ใน Web Worker แยกต่างหาก จึงไม่กระทบหน้าเว็บ
สรุป
- JavaScript รันโค้ดทีละชุดบน thread เดียว
- โค้ดปกติรันก่อน → Microtask ทั้งหมด → Macrotask ทีละงาน
Promise.thenและโค้ดหลังawaitเป็น Microtask จึงมาก่อนsetTimeoutเสมอ- ระวังอย่าสร้าง Microtask ไม่รู้จบ เพราะหน้าเว็บจะค้าง
พร้อมแล้วก็ไปทำ Quiz ท้ายบทด้านล่างเพื่อรับ XP ได้เลยครับ