Giới thiệu
Một trong khái niệm khá trừu tượng và khó hiểu trong Javascript là
Event Loop (vòng lặp xử lý sự kiến). Có nhiều người có thể đã lập trình
Javascript trong nhiều năm nhưng không thật sự hiểu chính xác Event loop
trong Javascript làm gì. Vì vậy, trong bài viết này tôi hy vọng sẽ làm
sáng tỏ hơn về Event loop trong Javascript, giúp các bạn cảm thấy nó
không còn phức tạp nữa.
Javascript trong browser
Khi nói về Javascript, chúng ta thường nghĩ về nó trong ngữ cảnh
một trình duyệt, bởi vì hầu hết chúng ta thường viết Javascript cho phía
client. Tuy nhiên, để chạy một ứng dụng web bất kỳ đều cần rât nhiều
công nghệ, ví dụ như Javascript Engine (như trong Chrome V8), một tập hợp các Web API (như DOM), Event Loop và Event Queue.
Khi nhìn vào danh sách trên, bạn có thể nghĩ "Ôi trời, trông có vẻ
phức tạp quá! ...", nhưng chúng ta sẽ sớm thấy được cách hoạt động cơ
bản của chúng và nó thực sự không quá phức tạp như bạn nghĩ.
Trước khi đi sâu vào Event Loop, chúng ta cần phải hiểu cơ bản về Javascript Engine và cách thức hoạt động của nó.
Javascript Engine
Có một vài sự khác biệt trong các phiên bản của Javascript Engine
nhưng phiên bản phổ biến nhất là Google Chrome V8 Engine (không chỉ hoạt
động trên trình duyệt mà còn hoạt động trên server thông qua NodeJS).
Nhưng chính xác thì Javascript Engine làm được những gì? Vâng, thực ra
rất đơn giản, công việc của nó là chạy xuyên suốt các dòng lệnh
Javascript trong một ứng dụng và xử lý chúng cùng một lúc. Đúng vậy,
cùng một thời điểm, có nghĩa là Javascript là một single-thread (đơn luồng). Do đó, nếu bạn đang chạy một dòng code trong Javascript mà cần một thời gian dài để trả về kết quả, nó sẽ block (chặn)
tất cả các đoạn code sau nó. Và chúng ta không muốn điều đó xảy ra, đặc
biệt là trên trình duyệt. Hãy thử tưởng tượng rằng bạn đang ở một trang
web và bấm vào một nút trên trang web, sau đó trang web bị treo. Bạn sẽ
thử bấm vào các nút khác nhưng không được, các nút khác không hoạt
động. Nguyên nhân của việc này (giả sử không có lỗi) là do các nút bấm
sau đó kích hoạt các đoạn Javascript nhưng đã bị chặn.
Vậy Javascript xử lý một dòng lệnh tại cùng một thời điểm như thế nào? Nó sử dụng một Call stack.
Bạn có thể hình dung về Call stack giống như một chồng đĩa xếp từ thấp lên cao, chiếc đĩa xếp cuối cùng ở đỉnh chồng đĩa, nó sẽ được lấy ra sớm nhất và chiếc đĩa ở ngay dưới nó sẽ là chiếc tiếp theo... Mỗi lệnh được nạp vào call stack theo trình tự xếp đĩa, còn trả về giống như lấy dần đĩa từ trên đỉnh
Hãy cùng xem một ví dụ:
/* Trong file main.js */
var firstFunction = function () {
console.log("I'm first!");
};
var secondFunction = function () {
firstFunction();
console.log("I'm second!");
};
secondFunction();
/* Kết quả:
* => I'm first!
* => I'm second!
*/
Và đây là chuỗi kết quả sẽ xảy ra với Call stack:
- Main.js chạy đầu tiên
- secondFunction được gọi
- Gọi secondFunction sẽ làm firstFunciton được gọi
- Chạy firstFunction sẽ làm chuỗi "I'm first!" được log ra trước, sau đó do không còn dòng code nào trong firstFunction nữa nên nó sẽ bị xóa khỏi Call stack
- secondFunction tiếp tục chạy và log ra chuỗi "I'm second!", sau đó do không còn dòng code nào nữa nên secondFunction cũng bị xóa khỏi Call stack
- Cuối cùng không còn dòng code nào trong main.js nữa nên nó cũng bị xóa khỏi Call stack
Vậy còn Event Loop thì sao?
Giờ chúng ta đã biết Call stack trong Javascript hoạt động như nào, hãy quay trở lại vấn đề blocking code.
Chúng ta đã biết là nên tránh blocking, nhưng làm thế nào để tránh? Rất
may cho chúng ta, Javascript cung cấp một cơ chế bất đồng bộ thông qua
các hàm callback. Nghe có vẻ hoành tráng đúng không? Tuy nhiên, các hàm bất đồng bộ callback
cũng giống như các hàm khác mà bạn dùng trong Javascript với thông báo
trước là nó sẽ không được thực thi ngay mà phải đợi đến thời điểm khác.
Nếu bạn đã từng sử dụng hàm setTimeout trong Javascript thì bạn đã quen
thuộc với các hàm callback! Hãy cùng xem một ví dụ:
/* Trong file main.js */
var firstFunction = function () {
console.log("I'm first!");
};
var secondFunction = function () {
setTimeout(firstFunction, 5000);
console.log("I'm second!");
};
secondFunction();
/* Kết quả:
* => I'm second!
* (Và sau 5 giây)
* => I'm first!
*/
Và sau đây là thứ tự hoạt động trong Call stack- Sau khi secondFunction được đặt vào Call stack, hàm setTimeout được gọi và cũng được đặt vào stack
- Ngay sau khi hàm setTimeout thực thi, browser đặt hàm callback của
setTimeout (trong trường hợp này là hàm firstFunction) vào trong một
Event table (bảng sự kiện). Hãy nghĩ Event table như một quầy đăng ký:
call stack sẽ đăng ký với Event table một hàm nào đó sẽ chỉ được thực
thi khi có một sự kiện cụ thể nào đó xảy ra. Và khi sự kiện đó xảy ra,
Event table chỉ đơn giản di chuyển hàm đã đăng ký sang Event queue (hàng
đợi). Event queue chỉ đơn giản là một địa điểm cho các hàm chờ được gọi
và di chuyển sang call stack.
Bạn cũng có thể hỏi: "Vậy chính xác thì khi nào các hàm trong Event
queue di chuyển sang Call stack?". Vâng, Javascript tuân theo một quy
luật rất đơn giản: Có một process liên tục kiểm tra xem có Call stack
nào rỗng không, và nếu rỗng thì nó sẽ kiểm tra Event queue có hàm nào
đang đợi hay không. Nếu có thì hàm đầu tiên trong Event queue sẽ được
gọi và di chuyển sang Call stack. Nếu Event queue rỗng thì process này
vẫn tiếp tục chạy vô thời hạn. Và những cái mà tôi vừa miêu tả chính là
Event loop!
- Quay trở lại với ví dụ trên, thực thi hàm setTimeout sẽ di chuyển hàm callback của nó (trong trường hợp này là firstFunction) vào Event table và đăng ký nó với một thời gian delay là 5 giây
- Chú ý rằng, một khi hàm callback được di chuyển đến Event table, không có bất kì đoạn code nào bị block. Browser sẽ không chờ 5 giây trước khi thực hiện tác vụ khác, mà thay vào đó nó sẽ thực thi dòng lệnh tiếp theo trong secondFunction là log ra chuỗi "I'm second!"
- Tại thời điểm này secondFunction và main.js đã thực thi xong
- 5 giây sau đó, Event table sẽ di chuyển hàm firstFunction sang Event queue
- Event loop liên tục kiểm tra Call stack và hiện tại nó phát hiện call stack đang rỗng nên gọi firstFunciton và tạo ra một Call stack mới
- Một khi firstFunction được thực thi xong, chúng ta trở lại trạng thái Call stack rỗng, Event table không còn hàm nào để lắng nghe và Event queue cũng rỗng
Kết luận
Chúng ta cần phải nắm vững cách thức hoạt động của các hàm bất đồng
bộ trong Javascript. Và tôi hy vọng rằng những giải thích trên đây sẽ
cung cấp cho các bạn những thông tin một cách rõ ràng, giúp ích cho phần
lớn công việc của chúng ta như những phát triển web.
Nguồn: www.altitudelabs.com
Ngày đăng: 06/05/2015
0 nhận xét:
Đăng nhận xét