Tôi nghĩ rằng tôi có thể minh họa điều này khá độc đáo. Vì nextTick
được gọi ở cuối hoạt động hiện tại, nên gọi nó một cách đệ quy có thể kết thúc việc chặn vòng lặp sự kiện tiếp tục. setImmediate
giải quyết điều này bằng cách bắn trong giai đoạn kiểm tra của vòng lặp sự kiện, cho phép vòng lặp sự kiện tiếp tục bình thường.
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
nguồn: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Lưu ý rằng giai đoạn kiểm tra là ngay sau giai đoạn thăm dò ý kiến. Điều này là do giai đoạn thăm dò ý kiến và các cuộc gọi lại I / O là nơi có nhiều khả năng các cuộc gọi của bạn setImmediate
sẽ chạy. Vì vậy, lý tưởng nhất là hầu hết các cuộc gọi đó sẽ thực sự ngay lập tức, không phải ngay lập tức như nextTick
được kiểm tra sau mỗi hoạt động và về mặt kỹ thuật tồn tại bên ngoài vòng lặp sự kiện.
Chúng ta hãy xem một ví dụ nhỏ về sự khác biệt giữa setImmediate
và process.nextTick
:
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
step(iteration + 1); // Recursive call from setImmediate handler.
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
});
}
step(0);
Giả sử chúng ta vừa chạy chương trình này và đang thực hiện bước lặp đầu tiên của vòng lặp sự kiện. Nó sẽ gọi vào step
hàm với số lần lặp bằng không. Sau đó, nó sẽ đăng ký hai xử lý, một cho setImmediate
và một cho process.nextTick
. Sau đó, chúng tôi gọi đệ quy hàm này từ setImmediate
trình xử lý sẽ chạy trong giai đoạn kiểm tra tiếp theo. Trình nextTick
xử lý sẽ chạy ở cuối hoạt động hiện tại làm gián đoạn vòng lặp sự kiện, vì vậy mặc dù đã được đăng ký lần thứ hai, nó sẽ thực sự chạy trước.
Thứ tự kết thúc là: nextTick
kích hoạt khi hoạt động hiện tại kết thúc, vòng lặp sự kiện tiếp theo bắt đầu, các giai đoạn vòng lặp sự kiện thông thường thực thi, setImmediate
kích hoạt và gọi đệ quy step
chức năng của chúng tôi để bắt đầu lại quá trình. Hoạt động hiện tại kết thúc, nextTick
hỏa hoạn, vv
Đầu ra của đoạn mã trên sẽ là:
nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9
Bây giờ, hãy chuyển cuộc gọi đệ quy step
của chúng tôi sang nextTick
xử lý thay vì setImmediate
.
function step(iteration) {
if (iteration === 10) return;
setImmediate(() => {
console.log(`setImmediate iteration: ${iteration}`);
});
process.nextTick(() => {
console.log(`nextTick iteration: ${iteration}`);
step(iteration + 1); // Recursive call from nextTick handler.
});
}
step(0);
Bây giờ chúng tôi đã chuyển cuộc gọi đệ quy step
sang nextTick
xử lý, mọi thứ sẽ hoạt động theo một trật tự khác. Lặp lại đầu tiên của chúng tôi về vòng lặp sự kiện chạy và gọi step
đăng ký một setImmedaite
trình xử lý cũng như một nextTick
trình xử lý. Sau khi hoạt động hiện tại kết thúc, nextTick
trình xử lý của chúng tôi sẽ thực hiện các cuộc gọi đệ quy step
và đăng ký một setImmediate
trình xử lý khác cũng như một nextTick
trình xử lý khác . Vì nextTick
trình xử lý kích hoạt sau thao tác hiện tại, việc đăng ký nextTick
trình xử lý trong nextTick
trình xử lý sẽ khiến trình xử lý thứ hai chạy ngay sau khi thao tác xử lý hiện tại kết thúc. Các nextTick
trình xử lý sẽ tiếp tục bắn, ngăn chặn vòng lặp sự kiện hiện tại tiếp tục. Chúng tôi sẽ vượt qua tất cảnextTick
xử lý trước khi chúng ta thấy một setImmediate
đám cháy xử lý duy nhất .
Đầu ra của đoạn mã trên kết thúc là:
nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9
Lưu ý rằng chúng tôi đã không làm gián đoạn cuộc gọi đệ quy và hủy cuộc gọi đó sau 10 lần lặp thì các nextTick
cuộc gọi sẽ tiếp tục đệ quy và không bao giờ để vòng lặp sự kiện tiếp tục sang giai đoạn tiếp theo. Đây là cách nextTick
có thể trở thành chặn khi được sử dụng đệ quy trong khi setImmediate
sẽ kích hoạt vòng lặp sự kiện tiếp theo và thiết lập một setImmediate
trình xử lý khác từ bên trong sẽ không làm gián đoạn vòng lặp sự kiện hiện tại, cho phép nó tiếp tục thực hiện các giai đoạn của vòng lặp sự kiện như bình thường.
Mong rằng sẽ giúp!
Tái bút - Tôi đồng ý với các nhà bình luận khác rằng tên của hai hàm có thể dễ dàng hoán đổi vì nextTick
âm thanh như phát ra trong vòng sự kiện tiếp theo thay vì kết thúc vòng lặp hiện tại và kết thúc vòng lặp hiện tại là "ngay lập tức "Hơn phần đầu của vòng lặp tiếp theo. Ồ, đó là những gì chúng ta nhận được khi đáo hạn API và mọi người phụ thuộc vào các giao diện hiện có.