Sự khác biệt giữa setImmediate
và setTimeout(0)
trong Node.js, chúng ta cần hiểu chi tiết hơn về cách event loop của Node.js hoạt động và cách mà các phases (giai đoạn) khác nhau trong event loop thực hiện các callback.
1. Event Loop trong Node.js
Event loop của Node.js bao gồm nhiều giai đoạn (phases) khác nhau, và mỗi giai đoạn có một vai trò cụ thể trong việc xử lý các callback từ các tác vụ bất đồng bộ. Dưới đây là các giai đoạn chính:
- Timers Phase: Xử lý các callback của
setTimeout
vàsetInterval
. Trong giai đoạn này, các callback củasetTimeout
được thực thi nếu thời gian chờ đã hết. - Pending Callbacks Phase: Xử lý các callback bị trì hoãn từ các tác vụ I/O đã hoàn thành.
- Idle, Prepare Phase: Dành cho các tác vụ nội bộ của Node.js. Không liên quan đến việc xử lý callback của người dùng.
- Poll Phase: Đây là giai đoạn quan trọng nhất, nơi Node.js sẽ chờ đợi các sự kiện I/O mới. Các callback từ các thao tác I/O sẽ được thực hiện tại đây, nếu không có gì xảy ra, event loop sẽ dừng lại để chờ.
- Check Phase: Đây là giai đoạn mà các callback của
setImmediate
được thực thi. Các callback củasetImmediate
được đẩy vào hàng đợi và sẽ được xử lý trong giai đoạn này. - Close Callbacks Phase: Xử lý các callback liên quan đến đóng tài nguyên (như
close
event của stream).
Vòng lặp này tiếp diễn liên tục cho đến khi không còn tác vụ nào cần xử lý.
2. Sự khác biệt giữa setTimeout(0)
và setImmediate
Cả setTimeout(0)
và setImmediate
đều được sử dụng để thực thi các callback không đồng bộ, nhưng chúng hoạt động ở các giai đoạn khác nhau của event loop:
setTimeout(0)
: Được đặt trong Timers phase, và sẽ chỉ được thực thi khi event loop đến giai đoạn này, sau khi các callback trong microtask queue và các tác vụ đồng bộ khác đã được hoàn thành.setImmediate
: Được đặt trong Check phase, và được thực thi ngay sau Poll phase, sau khi tất cả các callback I/O đã được xử lý.
3. Tại sao setImmediate
có thể được thực thi trước setTimeout(0)
?
Việc setImmediate
được thực thi trước setTimeout(0)
phụ thuộc vào giai đoạn mà chúng được đăng ký và sự tiến trình của event loop. Để hiểu rõ hơn, hãy xem xét hai trường hợp:
Trường hợp 1: Không có tác vụ I/O
Giả sử bạn gọi cả setTimeout(0)
và setImmediate
trong cùng một đoạn code:
setTimeout(() => { console.log('setTimeout'); }, 0); setImmediate(() => { console.log('setImmediate'); });
Khi event loop bắt đầu:
- Timers phase: Đây là giai đoạn đầu tiên sẽ được thực thi trong vòng lặp sự kiện. Nếu không có gì khác,
setTimeout(0)
sẽ được thực thi ngay lập tức vì thời gian chờ là 0. - Check phase: Sau khi xử lý hết các callback của giai đoạn timers,
setImmediate
sẽ được thực thi.
Vì vậy, nếu không có tác vụ I/O, setTimeout(0)
sẽ được thực thi trước setImmediate
trong trường hợp này.
Kết quả:
setTimeout setImmediate
Trường hợp 2: Có tác vụ I/O
Nếu có một tác vụ I/O hoặc bất kỳ tác vụ nào thực hiện qua Poll phase, thì setImmediate
có thể được thực thi trước setTimeout(0)
. Ví dụ:
const fs = require('fs'); fs.readFile('example.txt', () => { setTimeout(() => { console.log('setTimeout'); }, 0); setImmediate(() => { console.log('setImmediate'); }); });
Trong ví dụ này, sự kiện readFile
sẽ khiến Node.js đi qua nhiều giai đoạn của event loop:
- Poll phase: Sau khi tác vụ I/O hoàn thành (đọc file trong trường hợp này), callback của
fs.readFile
sẽ được thực hiện trong Poll phase. Tại thời điểm này:- Callback của
setImmediate
sẽ được xếp vào hàng đợi và sẽ được thực thi trong Check phase, ngay sau khi Poll phase kết thúc. - Callback của
setTimeout(0)
sẽ được xếp vào hàng đợi để thực thi trong Timers phase, và sẽ chỉ được thực thi trong vòng lặp sự kiện tiếp theo.
- Callback của
- Check phase: Khi đến Check phase, Node.js sẽ thực thi
setImmediate
trước, vì đây là giai đoạn màsetImmediate
hoạt động.
Kết quả trong trường hợp này:
setImmediate setTimeout
4. Tóm tắt sự khác biệt
setTimeout(0)
:- Được xử lý trong Timers phase.
- Thời gian chờ là tối thiểu 0ms, nhưng vẫn phải chờ đến khi
Timers phase
chạy trong event loop. - Nếu có các tác vụ I/O trong Poll phase,
setTimeout(0)
có thể bị trì hoãn đến vòng lặp sự kiện tiếp theo.
setImmediate
:- Được xử lý trong Check phase, sau khi tất cả các callback I/O đã được xử lý.
- Được ưu tiên hơn trong trường hợp có I/O, vì nó được thực thi ngay sau Poll phase trong cùng vòng lặp sự kiện.
- Trường hợp không có I/O:
setTimeout(0)
thường chạy trướcsetImmediate
. - Trường hợp có I/O:
setImmediate
thường chạy trướcsetTimeout(0)
.
5. Ứng dụng thực tế
- Sử dụng
setImmediate
: Khi bạn muốn đảm bảo rằng một callback sẽ được thực thi sau khi tất cả các tác vụ I/O trong event loop đã hoàn thành nhưng trước khi các tác vụsetTimeout(0)
được thực hiện trong vòng lặp tiếp theo. - Sử dụng
setTimeout(0)
: Khi bạn cần thực thi một callback nhanh chóng sau một khoảng thời gian ngắn (gần như ngay lập tức), nhưng có thể bị trì hoãn nếu có tác vụ I/O đang chạy.
Việc chọn setTimeout(0)
hay setImmediate
phụ thuộc vào ngữ cảnh cụ thể và cách bạn muốn các callback được lên lịch trong event loop của Node.js.

Dương Trần Hà, hiện mình đang là kỹ công nghệ phần mềm và cũng là giám đốc thành lập công ty DTH Solutions. Mình có nhiều năm kinh nghiệm, kiến thức chuyên môn lập trình, nodejs, nestjs, laravel, yii2, reactjs, nextjs. Mình đã phát triển rất nhiều dự án thực tế cho doanh nghiệp, cơ quan. Mình đã đạt được một số thành công nhỏ, đồng thời mình vẫn đang tiếp tục học tập để trau dồi kiến thức mỗi ngày. Mình rất yêu thích công nghệ, đam mê chia sẻ những kiến thức, thông tin hữu ích cho mọi người.