1. Event Loop là gì?
JavaScript là một ngôn ngữ đơn luồng (single-threaded), có nghĩa là nó chỉ có một luồng chính để thực thi code. Để xử lý tác vụ bất đồng bộ (asynchronous) mà không chặn chương trình, JavaScript sử dụng Event Loop.
Event Loop là cơ chế giúp JavaScript quản lý các tác vụ bất đồng bộ như setTimeout, fetch, event listener, và promises bằng cách điều phối các hàng đợi (queues) khác nhau.
2. Call Stack, Web APIs, Task Queue và Microtask Queue
Call Stack (Ngăn xếp gọi hàm)
Call Stack là nơi JavaScript thực thi các hàm theo nguyên tắc LIFO (Last In, First Out).
function foo() { console.log("Foo");
} function bar() { foo();
} bar();
Quá trình thực thi:
bar()
được đưa vào Call Stack.bar()
gọifoo()
,foo()
được đưa vào Call Stack.foo()
thực thi xong, bị pop khỏi Call Stack.bar()
thực thi xong, bị pop khỏi Call Stack.
Web APIs
Các hàm như setTimeout
, fetch
, và event listeners không chạy trong Call Stack mà được đưa đến Web APIs.
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
console.log("End");
Output:
Start
End
Timeout
Dù setTimeout
có thời gian 0ms, nó vẫn không chạy ngay lập tức mà được đưa vào Web APIs rồi Task Queue.
Task Queue (Macro-task Queue)
Task Queue chứa các macro-task như:
setTimeout
setInterval
setImmediate
(Node.js)I/O Callbacks
Event Loop chỉ lấy một task từ Task Queue mỗi lần và đưa vào Call Stack nếu Call Stack trống.
Microtask Queue (Priority Queue)
Microtask Queue có độ ưu tiên cao hơn Task Queue, chứa:
- Promises (resolve, reject)
- MutationObserver
- queueMicrotask
console.log("Start"); setTimeout(() => console.log("Timeout"), 0); Promise.resolve().then(() => console.log("Promise")); console.log("End");
Output:
Start
End
Promise
Timeout
Microtask (Promise.resolve()
) luôn chạy trước Task (setTimeout
).
3. Cách Event Loop vận hành
- Thực thi Call Stack (synchronous code).
- Xử lý tất cả microtasks (Promise, queueMicrotask, MutationObserver).
- Xử lý một task từ Task Queue (setTimeout, I/O, events).
- Lặp lại quá trình trên.
Event Loop tiếp tục chạy cho đến khi không còn code nào để xử lý.
4. Async/Await và Event Loop
async/await
giúp code bất đồng bộ dễ đọc hơn nhưng vẫn tuân theo Event Loop.
async function fetchData() { console.log("Start Fetch"); await new Promise(resolve => setTimeout(resolve, 1000)); console.log("End Fetch");
} console.log("Before Fetch");
fetchData();
console.log("After Fetch");
Output:
Before Fetch
Start Fetch
After Fetch
End Fetch
Giải thích:
fetchData()
gọiconsole.log("Start Fetch")
→ Call Stack.await
tạm dừngfetchData()
, trả control lại Event Loop.console.log("After Fetch")
chạy.- Sau 1s,
console.log("End Fetch")
chạy.
5. Tổng kết
- JavaScript là single-threaded nhưng có thể xử lý bất đồng bộ nhờ Event Loop.
- Microtasks (Promises) luôn chạy trước Macro-tasks (setTimeout, I/O).**
- Async/Await giúp xử lý bất đồng bộ dễ đọc hơn nhưng không thay đổi cơ chế Event Loop.
Xem thêm các bài viết tại: vunguyenit.site