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) // bỏ đói I/O mãi mãi
}
loop()
// các HTTP request đang chờ không bao giờ được phục vụsetImmediate 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) // nhường lượt giữa các vòng, I/O vẫn chạy
}
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 khi 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 hoá để callback luôn bất đồng bộ, hoặc phát lỗi ngay sau khi constructor trả về.