Skip to content
Home
Go back

Event Loop: Call Stack, Task Queue và Microtask Queue

Mục lục

Xem mục lục

Vì sao cần Event Loop

JavaScript chạy đơn luồng (single-threaded), tại một thời điểm chỉ thực thi được một đoạn code. Nếu mọi tác vụ đều chạy đồng bộ, một request mạng chậm hay một timer sẽ chặn đứng toàn bộ chương trình cho tới khi xong.

Event Loop là cơ chế cho phép JavaScript giao các tác vụ tốn thời gian (timer, network, đọc file…) ra ngoài luồng chính, rồi quay lại xử lý kết quả khi luồng chính rảnh, mà không cần nhiều luồng thật sự.

Các thành phần trong Event Loop

Event Loop vận hành dựa trên bốn thành phần chính, phối hợp với nhau theo một vòng lặp cố định:

Các phần tiếp theo đi vào chi tiết từng thành phần và thứ tự phối hợp giữa chúng.

Call Stack

Call Stack là nơi lưu các function đang được thực thi, hoạt động theo cơ chế LIFO (vào sau ra trước). Khi một function được gọi, nó được đẩy (push) lên stack. Khi function chạy xong, nó bị lấy ra (pop) khỏi stack.

function a() {
  b();
}
function b() {
  console.log("b chạy");
}
a();

Thứ tự trên stack: a được push, bên trong a gọi b nên b được push tiếp, b chạy xong bị pop, rồi a mới bị pop.

Web APIs / Node APIs

Các tác vụ bất đồng bộ như setTimeout, fetch, đọc file (fs trong Node.js) không được xử lý trực tiếp trong Call Stack. Chúng được giao cho môi trường chạy (browser hoặc Node.js) xử lý bên ngoài luồng chính. Khi tác vụ đó hoàn tất, callback tương ứng được đưa vào một hàng đợi, chờ tới lượt chạy lại trong Call Stack.

Task Queue (Macrotask Queue)

Task Queue chứa callback của các tác vụ như setTimeout, setInterval, sự kiện DOM, I/O callback. Mỗi lần Call Stack rỗng, Event Loop lấy đúng một task từ hàng đợi này đưa vào Call Stack để chạy.

Microtask Queue

Microtask Queue chứa callback của Promise.then/catch/finally và queueMicrotask. Hàng đợi này có độ ưu tiên cao hơn Task Queue.

Cơ chế vòng lặp

Mỗi vòng lặp của Event Loop diễn ra theo thứ tự:

  1. Chạy code đồng bộ cho tới khi Call Stack rỗng.
  2. Lấy toàn bộ Microtask Queue ra chạy hết, kể cả các microtask mới sinh ra trong lúc đang chạy microtask khác.
  3. Lấy một task từ Task Queue đưa vào Call Stack để chạy.
  4. Quay lại bước 2.

Nói cách khác, Microtask Queue luôn được dọn sạch trước khi Event Loop lấy task tiếp theo từ Task Queue.

Demo Event Loop Simulator

Bấm “Tiếp” để xem từng bước Call Stack, Web APIs, Task Queue, Microtask Queue thay đổi thế nào, hoặc chọn ví dụ khác từ danh sách để xem trường hợp new Promise() với executor chạy đồng bộ nhưng resolve() gọi bên trong setTimeout.

Call Stack
    Web APIs
      Task Queue
        Microtask Queue
          Console

          Tổng kết


          Share this post:

          Previous Post
          Thu hồi JWT: Cái giá của việc chọn stateless
          Next Post
          Closure trong JavaScript: Cơ chế và ứng dụng thực tế