JavaScript là ngôn ngữ đơn luồng, nghĩa là nó chỉ có thể thực hiện một tác vụ tại một thời điểm. Tuy nhiên, nhờ vào event loop, JavaScript có thể thực hiện các task bất đồng bộ giúp không gián đoạn trải nghiệm người dùng. Event loop là một thành phần quan trọng giúp JavaScript có thể thực hiện các task bất đồng bộ.
Cơ chế hoạt động của event loop bao gồm các thành phần chính sau
Memory heap là vùng nhớ lưu các biến tham chiếu(object, array, function).
Nơi lưu trữ các hàm được gọi. Khi một hàm được gọi, nó được đẩy vào(push) call stack và khi hàm này hoàn thành, nó sẽ được loại bỏ(pop) khỏi stack. Call stack hoạt động theo cơ chế FILO (First In Last Out), hàm được đẩy vào stach trước sẽ ra sau.
Đầu tiên function longRnningTask
sẽ được gọi và đẩy vào stack, code trong hàm được thực thi, sẽ mất thời gian xử lý vòng for rồi đến console.log()
, console.log()
thực thi xong sẽ bị đẩy ra khỏi stack.
Sau đó đến hàm longRnningTask
theo cơ chế first in last out, longRnningTask
thực thi xong bị đẩy ra khỏi stack và hàm imporantTask
được đẩy vào stack để thực thi.
Là nơi để xử lý các task bất đồng bộ. Các task vụ liên quan đến DOM, AJAX, setTimeout, fetch, promise sẽ không xử trong call stack, nó được call task được chuyển sang web api để xử lý bất đồng bộ. Khi web api xử lý xong các callback fuction sẽ được chuyển vào callback queue.
Nơi lưu trữ các hàm callback từ Web API khi chúng hoàn thành. Có 2 loại callback queue là callback queue và promise queue. Khi web api thực thi xong các hàm callback được đẩy vào promise queue nếu là code promise, async/await, còn lại sẽ đẩy vào callback queue. Promise queue sẽ có độ ưu tiên cao hơn callback queue.
Các callback queue thực hiện theo cơ chế first in first out, cái nào vào trước ra trước.
Event loop sẽ được run liên tục nó sẽ kiểm tra khi nào call stack empty tức là tất cả các code đồng bộ đã được thực thi. Lúc này event loop sẽ lấy hàm callback đầu tiên trong callback queue để đẩy lên call stack thực thi. Event loop sẽ ưu tiên thực thi hết callback trong promise queue, rồi mới đến callback queue. Vì vậy mà code trong promise sẽ được thực thi trước code trong settimeout.
Nhờ vào event loop, JavaScript có thể xử lý hiệu quả các tác vụ phức tạp mà không làm đứng hay chậm trình duyệt, dù là ngôn ngữ đơn luồng.
Giải thích 1 ví dụ đơn giản về event loop.
const foo = () => console.log("First");
const bar = () => setTimeout(() => console.log("Second"), 500);
const baz = () => console.log("Third");
bar();
foo();
baz();
// Ouput: First, Third, Second
Hàm bar được gọi xử lý trong call stack
, liên quan đến timer call task không thực thi được đẩy sang web api
, web api xử lý timer sau 500 minisecond đẩy hàm callback vào callback queue
. Cùng lúc đó fucntion foo được gọi log ra First, foo thực thi xong xoá khỏi call stack, baz được gọi log ra Third. Khi call stack empty tất cả các code đồng bộ chạy xong. Event loop
sẽ đồng bộ call stack và callback queue, log ra Second.
VD2.
const foo = () => console.log("2");
const bar = () => setTimeout(() => console.log("3"), 500);
const baz = () => console.log("4");
Promise.resolve()
.then(() => {
console.log('1');
});
bar();
foo();
baz();
// Ouput 2,4,1,3
Đầu tiên các code đồng bộ sẽ được thực thi, sau đó đến promise queue cuối cùng là callback queue.