Cơ chế đơn luồng, bất đồng bộ và thực thi của JavaScript

Giải thích về mô hình đơn luồng

JavaScript được thiết kế theo mô hình đơn luồng để đảm bảo tính nhất quán khi thao tác DOM. Nếu cho phép đa luồng, việc đồng bộ hóa tài nguyên DOM sẽ dẫn đến tình trạng race condition. Ví dụ:


document.getElementById("btn").addEventListener("click", () => {
  console.log("Xử lý click 1");
});

document.getElementById("btn").addEventListener("click", () => {
  console.log("Xử lý click 2");
});

Cả hai sự kiện click đều được xếp vào hàng đợi và xử lý tuần tự trên cùng một luồng chính.

Tại sao cần bất đồng bộ?

Việc thực thi đồng bộ có thể gây treo giao diện. Xét ví dụ:


function heavyTask() {
  let sum = 0;
  for (let i = 0; i < 1e9; i++) {
    sum += i;
  }
  return sum;
}

console.log("Bắt đầu");
heavyTask();
console.log("Kết thúc");

Kết quả: "Bắt đầu" được in ngay lập tức, sau đó là thời gian chờ dài để tính toán, cuối cùng mới in "Kết thúc".

Cơ chế thực thi bất đồng bộ

  • Call Stack: Ngăn xếp thực thi chứa các hàm đang chạy
  • Callback Queue: Hàng đợi chứa các hàm bất đồng bộ đã sẵn sàng
  • Event Loop: Vòng lặp kiểm tra và chuyển hàm từ hàng đợi vào ngăn xếp

Phân loại nhiệm vụ

JavaScript phân biệt hai loại nhiệm vụ:

LoạiVí dụ
MicrotaskPromise.then, MutationObserver
MacrotasksetTimeout, setInterval, DOM events

Thứ tự ưu tiên thực thi

Xét đoạn mã sau:


console.log("Start");

setTimeout(() => {
  console.log("Timeout");
}, 0);

Promise.resolve().then(() => {
  console.log("Promise");
});

console.log("End");

// Kết quả: Start → End → Promise → Timeout

Giải thích: Microtask (Promise) luôn được ưu tiên hơn Macrotask (setTimeout) trong cùng một vòng lặp sự kiện.

Ứng dụng thực tế trong framework

Ví dụ trong Vue.js:


this.items.push("new item");
this.$nextTick(() => {
  // Mã chạy sau khi DOM cập nhật
  console.log(document.getElementById("list").innerHTML);
});

Hàm $nextTick tận dụng hàng đợi Microtask để đảm bảo DOM đã được cập nhật trước khi thực thi callback.

Thẻ: JavaScript event-loop asynchronous-programming promise async-await

Đăng vào ngày 25 tháng 6 lúc 11:40