Vì hai hàng đợi này được xử lý theo cách khác nhau.
nextTick queue được xả cạn hoàn toàn sau mỗi thao tác, trước khi event loop được phép đi tiếp sang phase kế. Nếu callback lại đăng ký thêm nextTick, hàng đợi không bao giờ rỗng → event loop không bao giờ tới được phase poll. Kết quả: I/O không được xử lý, timer không chạy, server ngừng nhận request. Tiến trình vẫn "sống" và CPU 100%, nên thoạt nhìn khó chẩn đoán hơn một crash.
let i = 0
function loop() {
i++
process.nextTick(loop) // starves I/O forever
}
loop()
// pending HTTP requests are never servedsetImmediate thì khác: callback đăng ký trong lúc đang chạy phase check sẽ được xếp cho vòng lặp sau, không phải vòng hiện tại.
Nhờ đó mỗi lần đệ quy, event loop vẫn đi qua phase poll và xử lý I/O.
function loop() {
setImmediate(loop) // yields between iterations, I/O still runs
}
loop()Lưu ý mở rộng: vòng lặp promise microtask (Promise.resolve().then(loop)) cũng gây đói tương tự, vì microtask queue cũng được xả cạn trước khi trả điều khiển cho event loop.
Quy tắc thực dụng:
- Đệ quy hoặc chia nhỏ tác vụ dài → dùng setImmediate.
- process.nextTick chỉ dùng cho việc rất ngắn, không đệ quy: chuẩn hóa để callback luôn bất đồng bộ, hoặc phát lỗi ngay sau khi constructor trả về.