Mục lục

Event Loop & Concurrency: Tại sao JS lại nhanh?

Phân tích cơ chế xử lý bất đồng bộ, Microtasks vs Macrotasks và cách JS xử lý hàng triệu request trên một luồng duy nhất.

Sai lầm lớn nhất của các lập trình viên Junior là nghĩ rằng JavaScript chạy đa luồng vì nó có thể xử lý async/await. Thực tế, JS là Single-threaded (đơn luồng). Sức mạnh của nó nằm ở Event Loop.

1. Kiến trúc Runtime

JS Engine (V8) không chạy độc lập. Nó nằm trong một môi trường (Browser hoặc Node.js) cung cấp các Web APIs hoặc C++ APIs.

  • Call Stack: Nơi thực thi mã JS đồng bộ.
  • Web APIs: Nơi xử lý các tác vụ tốn thời gian (setTimeout, Fetch API, DOM Events).
  • Callback Queue (Macrotasks): Nơi chứa các callback từ setTimeout, setInterval.
  • Microtask Queue: Nơi chứa các callback từ Promises, queueMicrotask. Ưu tiên cao hơn Macrotasks.

Event Loop: Kitchen Analogy

2. Event Loop vận hành như thế nào?

Cơ chế cực kỳ đơn giản:

  1. Kiểm tra Call Stack có trống không?
  2. Nếu trống, thực thi tất cả các task trong Microtask Queue.
  3. Sau đó, lấy một task từ Macrotask Queue và đẩy vào Call Stack.
  4. Lặp lại.

3. Thử nghiệm: Microtasks vs Macrotasks

Hãy chạy bài Lab dưới đây để thấy thứ tự thực thi thực tế. Đây là câu hỏi kinh điển trong các buổi phỏng vấn Senior.

4. Senior Note: Đừng làm "nghẽn" Event Loop

Vì JS chỉ có một luồng, nếu bạn thực hiện một vòng lặp tính toán quá nặng (vd: xử lý ảnh, mã hóa) trực tiếp trên Call Stack, trình duyệt sẽ bị "đứng hình" (UI freeze).

Giải pháp của Senior:

  • Chia nhỏ task (Time slicing) bằng requestIdleCallback.
  • Sử dụng Web Workers để đẩy tính toán nặng sang một luồng xử lý thực sự khác.

So với Java (sử dụng nhiều Thread, tốn nhiều bộ nhớ cho mỗi Thread), kiến trúc Event Loop của JS giúp nó cực kỳ tiết kiệm tài nguyên và phù hợp cho các ứng dụng I/O intensive (như Web Server hoặc Real-time UI).

Quảng cáo
mdhorizontal