JavaScript: Event Loop
Ngày 23 tháng 3, 2026
Bạn đã bao giờ tự hỏi làm thế nào JavaScript — một ngôn ngữ đơn luồng (single-threaded) — lại có thể xử lý hàng nghìn yêu cầu cùng lúc mà không bị "treo"? Câu trả lời không nằm ở bản thân ngôn ngữ, mà nằm ở JavaScript Runtime Environment.
Hãy cùng bóc tách từng lớp của "củ hành" mang tên Event Loop này qua các mô phỏng trực quan dưới đây.
1. Call Stack: Trái tim của sự thực thi
Call Stack là một cấu trúc dữ liệu dạng LIFO (Last In, First Out). Nó theo dõi hàm nào đang được thực thi.
Cơ chế hoạt động:
- Khi bạn gọi một hàm, nó được "đẩy" (push) vào stack.
- Trình thông dịch thực thi hàm đó.
- Khi hàm trả về kết quả hoặc kết thúc, nó được "lấy ra" (pop) khỏi stack.
2. Web APIs: Cánh tay nối dài của Browser
JavaScript Engine (như V8) không làm việc một mình. Trình duyệt cung cấp các Web APIs để xử lý những tác vụ tốn thời gian như HTTP Request, Timers, hoặc DOM events.
Điều gì thực sự xảy ra?
JavaScript đẩy setTimeout vào Call Stack -> Nhận ra đây là Web API -> Đẩy nó sang cho Browser quản lý bộ đếm -> setTimeout ra khỏi stack ngay lập tức. Luồng thực thi tiếp tục chạy mà không đợi. Khi bộ đếm kết thúc, Browser đẩy Callback vào Task Queue.
3. Quá trình "Xếp hàng": Task Queue & Microtask Queue
Sau khi Browser xử lý xong tác vụ (ví dụ: đếm xong 2 giây hoặc nhận được dữ liệu từ API), nó không thể tự ý nhảy vào Call Stack. Nó phải đứng vào hàng đợi.
Task Queue (Macrotask Queue)
Dành cho: setTimeout, setInterval, setImmediate, I/O tasks.
Microtask Queue (Ưu tiên cao hơn)
Dành cho: Promises, async/await, process.nextTick, MutationObserver.
Quy tắc vàng: Event Loop sẽ ưu tiên quét sạch TOÀN BỘ Microtask Queue trước khi lấy MỘT task từ Task Queue.
4. Event Loop: Người điều phối tận tụy
Event Loop chỉ có một công việc duy nhất: Kiểm tra Call Stack. Nếu Call Stack trống, nó sẽ lấy tác vụ từ hàng đợi và đẩy vào Stack để thực thi.
Quy trình 4 bước:
- Check Stack Empty: Đợi cho đến khi không còn hàm nào đang chạy.
- Handle Microtasks: Ưu tiên xử lý hết sạch Microtask Queue.
- Pick Macrotask: Lấy duy nhất 1 Macrotask từ Task Queue.
- Push to Stack: Đưa tác vụ vào Stack để Engine thực thi.
5. Tại sao cần quan tâm?
Hiểu Event Loop giúp bạn tránh được những lỗi "tai hại" về hiệu năng:
- Đừng chặn Main Thread: Đừng chạy các thuật toán quá nặng (như xử lý ảnh, tính toán số lớn) trực tiếp trong Call Stack. Hãy dùng Web Workers.
- Thứ tự thực thi: Biết chắc chắn khi nào dữ liệu từ API sẽ được xử lý.
- Ưu tiên Microtask: Sử dụng
queueMicrotask()nếu bạn cần một tác vụ chạy ngay sau đoạn code hiện tại nhưng trước khi Browser render lại UI.
Tóm tắt trong 30 giây:
- Stack: Nơi code thực thi ngay lập tức.
- Web API: Nơi các tác vụ bất đồng bộ "tạm trú".
- Microtask Queue: "Vip" Queue (Promise).
- Task Queue: "Normal" Queue (setTimeout).
- Event Loop: Người gác cổng, chỉ mở cửa đẩy hàng đợi vào Stack khi Stack đã hoàn toàn rỗng.
Hy vọng bài viết này đã giúp bạn "giải mã" được sự kỳ diệu phía sau cách JavaScript vận hành!