Microtasks & Macrotasks
สองคิว หนึ่งลูป
หัวข้อที่มีชื่อว่า “สองคิว หนึ่งลูป”event loop ประมวลผล callback แบบ async จากสองคิวที่แยกกันด้วยลำดับความสำคัญต่างกัน:
Microtask queue — ประมวลผลหลังจากทุก task ก่อน macrotask ถัดไป แหล่งที่มา:
- callback ของ Promise
.then/.catch/.finally queueMicrotask(fn)
Macrotask queue (หรือเรียก task queue) — ประมวลผลทีละหนึ่ง task ต่อรอบ event loop แหล่งที่มา:
- callback ของ
setTimeout - callback ของ
setInterval - callback ของ I/O (network, file system)
กฎง่าย ๆ: หลังแต่ละ macrotask ทำเสร็จ เอนจินจะระบาย microtask queue ทั้งหมด ก่อนจะหยิบ macrotask ถัดไป
ลำดับการระบาย
หัวข้อที่มีชื่อว่า “ลำดับการระบาย”┌─────────────────────────────┐│ Execute one macrotask ││ (or the initial script) │└──────────────┬──────────────┘ │ ▼┌─────────────────────────────┐│ Drain all microtasks │◄──┐│ (including newly added) │ │ new microtasks└──────────────┬──────────────┘───┘ │ ▼┌─────────────────────────────┐│ Pick next macrotask │└─────────────────────────────┘เฉพาะ Node: process.nextTick และ setImmediate
หัวข้อที่มีชื่อว่า “เฉพาะ Node: process.nextTick และ setImmediate”Node.js เพิ่ม API การจัดคิวอีกสองตัวที่ต้องใช้ Node runtime:
// ใช้ได้ใน Node.js เท่านั้น — ไม่ได้ในเบราว์เซอร์process.nextTick(function() { console.log('nextTick fires before other microtasks');});
setImmediate(function() { console.log('setImmediate fires after I/O callbacks in the current loop iteration');});callback ของ process.nextTick ถูกระบายก่อน microtask queue ส่วน setImmediate ทำงานในเฟส check ของ event loop ของ Node เนื่องจากทั้งสองต้องใช้ Node runtime จึงใช้ไม่ได้ในตัวรันใน browser ด้านล่าง — ให้รันในเครื่องหรือใน StackBlitz
เดโมจัดลำดับที่รันได้
หัวข้อที่มีชื่อว่า “เดโมจัดลำดับที่รันได้”เดโมนี้พิมพ์สี่บรรทัด ลองทายลำดับก่อนรัน:
console.logแบบ synchronous ทำงานทันทีบน call stacksetTimeout(..., 0)จัดคิว macrotaskPromise.resolve().then(...)จัดคิว microtask (Promise resolve อยู่แล้ว reaction จึงถูกจัดคิวแบบ synchronous)queueMicrotask(...)จัดคิวอีก microtask — หลังจาก reaction ของ Promise ที่จัดคิวไปข้างบนแล้ว
หลังสคริปต์ synchronous ทำเสร็จ microtask ทั้งหมดจะระบายตามลำดับ แล้ว macrotask ค่อยทำงาน
ผลลัพธ์จะเป็นลำดับนี้เสมอ:
1 sync— call stack2 microtask (Promise)— microtask ตัวแรกที่จัดคิว3 microtask (queueMicrotask)— microtask ตัวที่สองที่จัดคิว4 macrotask— ทำงานหลังจาก microtask queue ว่างแล้วเท่านั้น