Node.js event loop xử lý async ops theo phases: timers→poll→check — giữa mỗi phase flush toàn bộ microtask queue (nextTick → Promises); thứ tự: process.nextTick(C), Promise.then(B), setTimeout(A, 0) → chạy C, B, A.
Event loop là vòng lặp liên tục xử lý callbacks theo 6 phases:
- Timers: thực thi callbacks của
setTimeout/setIntervalđã đến hạn; - Pending callbacks: I/O errors từ vòng trước;
- Idle/prepare: internal use;
- Poll: fetch I/O events mới — nếu queue empty và không có timers pending, block tại đây chờ I/O;
- Check:
setImmediatecallbacks; - Close callbacks:
socket.on('close')
Giữa MỖI phase, Node.js xử lý toàn bộ microtask queue: Promise callbacks (then/catch) + queueMicrotask() + process.nextTick() (nextTick ưu tiên hơn Promise).
Starvation: nếu microtask queue không bao giờ empty (Promise resolve tạo Promise mới), event loop không bao giờ qua phase tiếp theo.
Ví dụ thực tế execution order: setTimeout(A, 0) → Promise.resolve().then(B) → process.nextTick(C) — thứ tự chạy: C, B, A.
Node.js event loop processes async ops through phases: timers→poll→check — microtask queue (nextTick → Promises) is flushed between every phase; execution order: process.nextTick(C), Promise.then(B), setTimeout(A, 0) → runs C, B, A.
The event loop is a continuously running cycle that processes callbacks across 6 phases:
- Timers: executes callbacks from
setTimeout/setIntervalthat have elapsed; - Pending callbacks: I/O errors from the previous cycle;
- Idle/prepare: internal use;
- Poll: fetches new I/O events — if the queue is empty and no timers are pending, blocks here waiting for I/O;
- Check:
setImmediatecallbacks; - Close callbacks:
socket.on('close')
Between EACH phase, Node.js processes the entire microtask queue: Promise callbacks (then/catch) + queueMicrotask() + process.nextTick() (nextTick has higher priority than Promises).
Starvation: if the microtask queue never empties (a resolved Promise creating new Promises), the event loop never advances to the next phase.
Practical execution order example: setTimeout(A, 0) → Promise.resolve().then(B) → process.nextTick(C) — execution order: C, B, A.