0

Cách Event Loop quản lý các tác vụ bất đồng bộ trong Javascript

Mở đầu

  • JavaScript là một ngữ lập trình chạy đơn luồng - single-threaded !!! Nếu vậy không phải một tác vụ chạy quá lâu thì sẽ ngăn chặn các tác vụ phía sau gây nên tình trạng trang web bị treo! 🤔
  • Nhưng thực chất khi truy cập, tương tác với các website trên trình duyệt chúng ta vẫn thấy mọi thứ được xử lý rất mượt mà!
  • Vậy phải có một cơ chế độc đáo để quản lý các tác vụ này mà không gây tắc nghẽn hệ thống?!
  • Đó chính là lúc Event Loop xuất hiện và làm chủ cuộc chơi 😎.

Event Loop là gì?

  • Event Loop là một thành phần của Javascript Runtime Environment (môi trường thực thi Javascript).
  • Event Loop giúp quản lý, thực thi các tác vụ một cách bất đồng bộ mà không lo việc phải chờ đợi lẫn nhau.
  • Chúng ta hãy cùng tìm hiểu chi tiết hơn về Javascript Runtime Environment, cách thức hoạt động và vai trò của Event Loop trong hệ thống này như thế nào nhé !

JavaScript Runtime Environment

  • JavaScript Runtime Environment là nơi để xử lý và thực thi các tác vụ liên quan đến JavaScript.
  • Để JavaScript có thể thực thi một cách mượt mà thì cần đến các thành phần chính sau:
Sơ đồ mô tả luồng thực thi Javascript và các thành phần của Javascript Runtime Enviroment

1. JavaScript Engine

  • Tất nhiên điều quan trọng tiên quyết là chúng ta phải có một trình biên dịch để dịch mã JavaScript thành mã nhị phân để máy tính có thể hiểu và thực thi.
  • Bạn có thể tìm hiểu chi tiết hơn trong bài viết sau để hiểu rõ hơn về cấu tạo của JavaScript Engine: V8 Engine trong Chrome và Node.js
  • Nhưng JavaScript Engine chỉ có thể xử lý được các đoạn mã thuần và chạy một cách đồng bộ (chạy theo nguyên lý hàng đợi Stack).
js

// Javascript Engine chỉ có thể xử lý các đoạn mã Javascript thuần như: 

// ghi log
console.log("Learning Javascript");

// khai báo biến
const a = 1
  • Để có thể xử lý được các đoạn mã phức tạp hơn như: kết nối đến giao thức Http, quản lý các tác vụ bất đồng bộ, các sự kiện để tác động đến DOM, CSDOM tree,... thì lúc này chúng ta cần sự hỗ trợ thêm của các thành phần khác.

2. Web / Browser Api

  • Web / Browser Api là các API cung cấp bởi Runtime Environment (Browser, Node.Js).
  • API (Application Programming Interface) chính là công cụ để Javascript có thể giao tiếp được với môi trường bên ngoài !

2.1. DOM

  • DOM (Document Object Model) là một tập hợp các phương thức giúp JavaScript giao tiếp, tương tác với HTML/CSS.
  • Các phương thức này được thực thi một cách đồng bộ (synchoronous).
js

// Tạo nội dụng HTML cho phần tử với id="mydiv"
document.getElementById("mydiv").innerHTML = "Hello Viblo!!!";

// Đổi màu chữ thành màu xanh dương cho phần tử với id="mydiv"
document.getElementById("mydiv").style.color = "blue";

2.2. Web Storage Api

  • Web Storage API giúp lưu trữ dữ liệu trên trình duyệt của người dùng theo hai dạng chính:
Loại Chức năng
localStorage Lưu dữ liệu vĩnh viễn, không bị mất khi đóng trình duyệt.
sessionStorage Lưu dữ liệu tạm thời, bị mất khi đóng tab trình duyệt.
js

// localStorage
localStorage.setItem("username", "ThanhEtn");
localStorage.getItem("username");

// sessionStorage
sessionStorage.setItem("token", "abc123");
sessionStorage.getItem("token");

2.3. Web Worker Api

  • Web Worker giúp xử lý công việc nặng trên background thread (một thread riêng biệt chạy trong nền), giữ cho main thread (UI) không bị chặn.
  • Một số tác vụ mà Web Worker có thể xử lý một cách hiệu quả:
Tác vụ Mô tả
Xử lý tính toán phức tạp Xử lý dữ liệu lớn, tính toán số nguyên tố
Xử lý dữ liệu lớn Parse file JSON lớn, xử lý dữ liệu log, thống kê số liệu
Gọi API & Xử lý dữ liệu Fetch, WebSockets, IndexedDB, Timer API
Nén & Giải nén dữ liệu Nén ảnh, video, file trước khi gửi đi
js

// Gửi yêu cầu Https đến server
async function getData () {
    const response = await fetch("https://viblo.asia/api/posts/newest?limit=20");
    return await response.json();
}

// SetTimeOut - Timer API
console.log("Chạy trong Main Thread");
setTimeout(() => {
    console.log("Đã chờ 3 giây trong Web Worker!");
}, 3000);
  • Vậy khi các tác vụ này đã được thực thi xong trong Web Worker thì thành phần nào sẽ đưa chúng trở lại Call Stack để thực thi 🤔?

3. Task Queue / MicroTask Queue

  • Các tác vụ bất đồng bộ (asynchoronous) sau khi đã thực thi xong trong Web Worker sẽ được đưa vào các hàng đợi đặt biệt là: Task Queue, MicroTask Queue.
  • Các hàng đợi (Queue) này được thực thi theo quy tắc: FIFO (First In, First Out). Tức là tác vụ nào được đưa vào hàng đợi trước sẽ được thực thi trước.
js

// Task 1 vào hàng đợi trước Task 2, nên nó sẽ được thực thi trước
setTimeout(() => console.log("Task 1"), 0);
setTimeout(() => console.log("Task 2"), 0);
  • MicroTask Queue có thứ tự ưu tiên cao hơn so với Task Queue. Các tác vụ trong MicroTask Queue sẽ được thi đến khi nào rỗng thì mới tới các tác vụ trong Task Queueđược thực thi.
js

console.log("Start");
setTimeout(() => console.log("Task Queue"), 0);
Promise.resolve().then(() => console.log("MicroTask Queue"));
console.log("End");
Trình tự thực thi
1. "Start" → Call Stack (thực thi ngay)
2. setTimeout() → Gửi vào Task Queue
3. Promise.then() → Gửi vào MicroTask Queue
4. "End" → Call Stack (thực thi ngay)
5. MicroTask Queue chạy trước → "MicroTask Queue"
6. Sau khi MicroTask Queue trống → "Task Queue" được thực thi.
  • Nhưng CallStack có phải lúc nào cũng trong trạng thái sẵn sàng để các tác vụ trong Queue có thể được đưa vào thực thi?

4. Event Loop

  • Event Loop chính là chiếc cầu nối giữa Call Stack với Task Queue, MicroTask Queue. Giúp đảm bảo các tác vụ bất đồng bộ xử lý một cách mượt mà dù Javascript chỉ chạy 1 luồng duy nhất 😎💯.
  • Các nhiệm vụ chính mà Event Loop thực hiện đó là:
  1. Kiểm tra trạng thái của Call Stack nếu trống sẽ tìm một tác vụ mới từ Queue để đẩy vào Call Stack thực thi.
  2. Ưu tiên lấy tác vụ từ MicroTask Queue trước. Xử lý từng tác vụ trong đây và đưa vào Call Stack để thực thi.
  3. Khi MicroTask Queue đã rỗng. Lấy từng tác vụ trong Task Queue đưa vào Call Stack để thực thi.
Sơ đồ mô tả cách Event Loop hoạt động

Kết luận

  • Javascript một ngôn ngữ chạy đơn luồng nhưng với một kiến trúc tuyệt vời của Javascript Runtime Enviroment đã có thể xử lý một cách mượt mà các tác vụ, tạo ra một trải nghiệm tuyệt vời cho người dùng.
  • Nhờ vào sự kết hợp vô cùng ăn ý giữa các thành phần. Đặc biệt trong đó là Event Loop giúp điều phối sự hoạt động của các tác vụ đồng bộ và bất đồng bộ với nhau.
  • Hy vọng qua bài viết, giúp các bạn có một cái nhìn chi tiết về cách thức Javascript được thực thi cũng như giải đáp được thắc mắc ở đầu bài viết về ngôn ngữ đơn luồng lại có thể xử lý được các tác vụ bất đồng bộ 🤩.

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí