Giải thích về setImmediate(), nextTick() và setTimeout(fn,0)
Quan niệm sai lầm về vấn đề setImmediate, nextTick và setTimeo trong nodejs Trước khi đi vào tìm hiểu, chúng ta sẽ cùng nhìn lại một số quan niệm sai lầm hoặc chưa rõ ràng về những hàm này. Nếu bạn cảm thấy đã chắc chắn thì có thể bỏ qua phần này 1. setImmediate chạy trước setTimeout (*ex1) ...
Quan niệm sai lầm về vấn đề setImmediate, nextTick và setTimeo trong nodejs
Trước khi đi vào tìm hiểu, chúng ta sẽ cùng nhìn lại một số quan niệm sai lầm hoặc chưa rõ ràng về những hàm này. Nếu bạn cảm thấy đã chắc chắn thì có thể bỏ qua phần này
1. setImmediate chạy trước setTimeout (*ex1)
Đây là một trong những quan niệm sai lầm phổ biến, chúng ta sẽ cùng thảo luận về quan niệm đúng trong phần sau của bài viết này, trước hết hãy thử xem quan niệm này sai ở điểm nào
// file: wrong-timeout-immediate.js setTimeout(function(){ console.log("Timeout"); }); setImmediate(function(){ console.log("Immediate"); });
Nếu quan niệm trên là đúng thì khi chạy chúng ta sẽ thấy tại console sẽ luôn hiển thị "Immediate" trước khi "Timeout" được hiển thị. Tuy nhiên trong thực tế thì khi chạy file trên, chúng ta sẽ không dự đoán trước được kết quả hiển thị. Nếu bạn chạy node wrong-timeout-immediate.js bạn sẽ thấy ngay được rằng không phải lúc nào "Immediate" cũng được hiển thị trước "Timeout"
setImmediate() đẩy hàm callback lên đầu của job queue
//file: wrong-immediate-callback.js setTimeout(function() { console.log("TIMEOUT 1"); setImmediate(function() { console.log("SETIMMEDIATE 1"); }); }, 0); setTimeout(function() { console.log("TIMEOUT 2"); setImmediate(function() { console.log("SETIMMEDIATE 2"); }); }, 0); setTimeout(function() { console.log("TIMEOUT 3"); }, 0);
Nếu quan niệm này là đúng, khi chạy node immediate-callback.js ta sẽ thu về được kết quả là
TIMEOUT 1 SETIMMEDIATE 1 TIMEOUT 2 SETIMMEDIATE 2 TIMEOUT 3
Tuy nhiên, kết quả nhận được lại là
TIMEOUT 1 TIMEOUT 2 TIMEOUT 3 SETIMMEDIATE 1 SETIMMEDIATE 2
nextTick() thực hiện lời gọi hàm callback tại lượt tiếp theo (vòng lặp)
Thực ra thì cả process.nextTick() và setImmediate() đều được đặt tên không sát với ý nghĩa của chúng. Nếu chúng ta tráo đổi tên của hai hàm này thì chúng sẽ sát nghĩa với chức năng của mỗi hàm hơn. Xét về mặt tính năng thì process.nextTick() thực ra là cách để thực hiện lời gọi hàm callback ngay lập tức. Hàm callback trong setImmediate() sẽ được thực thi trong vòng lặp tiếp theo
Cách thức hoạt động của node.js event loop
Cách duy nhất để có thể hiểu về luồng làm việc và sự khác biệt của 3 hàm này đó là phải hiểu được về event loop. Dưới đây là một mô tả ngắn về event loop
Trong hình ảnh trên, mỗi hình chữ nhật đại diện cho một giai đoạn và event loop duyệt qua lặp lại từng giai đoạn một. Khởi đầu là từ Timers và kết thúc lại Close Callbacks. Ngoài ra thì cũng có thêm một thứ gọi là nextTickQueue nằm ở giữa, tuy nhiên bản thân nó lại không phải là thành phần của event loop. Mỗi giai đoạn bên trên đều được đính kèm theo một queue (hàng đợi). Khi event loop chạy vào một giai đoạn cụ thể, nó sẽ target vào việc thực thi những callbacks/tasks trong queue của giai đoạn đó.
Mô tả ngắn gọn về các giai đoạn trong event loop
Tỉmer: xử lý những callbacks được gán vào bởi setTimeout và setInterval sau khi hết ngưỡng thời gian chờ
I/O callbacks: xử lý tất cả callbacks, ngoại trừ những callbacks được thiết lập bởi setTimeout, setInterval và setImmediate. Tại đây cũng không tồn tại Close callbacks
Idle, prepare: Được dùng bên trong nội bộ của event loop
Poll: Tìm kiếm và lấy ra những sự kiện I/O mới (ltg: điều này khiến Node trở thành một thanh niên cực ngầu