JavaScript: Event Loop

Hoang Vu,JavaScript

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.

Javascript Runtime Environment
Call Stack
Web APIs
Task Queue
Microtask Queue
Event Loop

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:

  1. Khi bạn gọi một hàm, nó được "đẩy" (push) vào stack.
  2. Trình thông dịch thực thi hàm đó.
  3. Khi hàm trả về kết quả hoặc kết thúc, nó được "lấy ra" (pop) khỏi stack.
Runtime Visualizer
Step 0/10
Source Code
Editor
1234567891011
Call Stack (LIFO)
Stack Empty
Live Console
Trạng thái luồng:
IDLE
Mô tả cơ chế:
Chờ thực thi...

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.

JS Engine (Stack)
Web APIs (Browser)
Task Queue

Đ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.

Microtask Queue
(VIP)
Promise 1
Promise 2
Task Queue
(Macro)
setTimeout
Call Stack Execution
Ready

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.

Task Queue
task 1
task 2
Done!
Call Stack
Stack Empty
STEP 1: Check Stack Empty
STEP 2: Take Task From Queue
STEP 3: Push Task to Stack
STEP 4: Wait For Task Finish

Quy trình 4 bước:

  1. Check Stack Empty: Đợi cho đến khi không còn hàm nào đang chạy.
  2. Handle Microtasks: Ưu tiên xử lý hết sạch Microtask Queue.
  3. Pick Macrotask: Lấy duy nhất 1 Macrotask từ Task Queue.
  4. 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:

  1. Đừ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.
  2. Thứ tự thực thi: Biết chắc chắn khi nào dữ liệu từ API sẽ được xử lý.
  3. Ư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!

2026 © @hoag/blog.