Cơ chế hoạt động của Javascript và NodeJS

1111
27-02-2018
Cơ chế hoạt động của Javascript và NodeJS

Trong bài viết này, Bizfly Cloud xin giới thiệu cơ chế hoạt động của Javascript và NodeJS ngay tại bài viết này nhé. 

Non-blocking I/O 

Trong JavaScript, hầu hết các lời gọi I/O đều là non-blocking. Nghĩa là khi có HTTP request, truy xuất dữ liệu trong DB hoặc đọc ghi vào bộ nhớ thì hệ thống sẽ không tạm dừng (blocking) các đoạn code tiếp theo (như các ngôn ngữ server khác PHP, Ryby,...) mà sẽ trao quyền thực thi những lời gọi I/O này cho hệ thống và thực thi những đoạn code tiếp theo, khi hệ thống đã thực thi xong những lời gọi hệ thống này thì hàm callback truyền vào sẽ tự động được gọi.

Ví dụ về blocking trong Ruby:

response = Faraday.get 'http://www.google.com'

puts response

puts 'Done!'

Trình tự hệ thống thực hiện đoạn code trên là:

  • Request tới http://www.google.com, lúc này hệ thống sẽ tạm dừng (blocking) để chờ kết qủa của request này trả về.
  • Thông tin trả về và được gán vào biến response.
  • Hiển thị response được trả về lên màn hình.
  • Hiển thị 'Done' lên màn hình.

Thứ tự thực hiển của các ngôn ngữ server thông thường (như PHP, Ruby) sẽ thực hiện kiểu synchronous (đồng bộ) từ trên xuống dưới như flow trên. Còn dưới đây là ví dụ về trình tự thực thi trong NodeJS (JavaScript):

request('http://www.google.com', function(error, response, body) {

console.log(body);

});

console.log('Done!');

Lưu ý: Đoạn code trên cũng sẽ được gọi theo thứ tự từ trên xuồng dưới.

• Câu lệnh đầu tiên sẽ gọi request tới http://www.google.com, không như trên, ở đây hệ thống sẽ không đợi kết qủa trả về mới thực thi tiếp. Hệ thống sẽ truyền hàm callback ở trên vào event loop để khi hàm request có kết qủa trả về thì sẽ thực thi hàm callback này ngay. Sau khi truyền hàm callback vào event loop thì hệ thống sẽ tiếp tục thực hiện câu lệnh phía dưới.

• In ra màn hình 'Done!'

• Vào thời điểm nào đó sau đó, khi request có kết qủa trả về thì hàm callback sẽ được gọi và in ra body của response.

Code như trên gọi là assynchronous (bất đồng bộ), nghĩa là hệ thống có thể thực hiện các đoạn code của mình một cách đồng thời(như vd trên Done! được in ra trước body trả về).

Event Loop

Trình khởi chạy của JavaScript có một hàng đợi (queue) chứa các messages, các messages này được gắn liền với các hàm callback truyền vào. Mỗi khi gặp câu lệnh có callback truyền vào thì message gắn với callback đó sẽ được đẩy vào queue, và khi các event được trigger( ví dụ như event click, event request có response trả về) thì hệ thống sẽ gọi hàm callback tương ứng để thực thi. Khi một event ví dụ như click vào button nhưng không truyền vào callback thì sẽ không có message nào được đẩy vào queue, nghĩa là chỉ những event có callback tryền vào thì mới được đẩy vào queue.

Ảnh 3.

Hình trên mô tả hoạt động của Event Loop. Khi có trigger(sự kiện click hoặc request ở vd trên thực hiện xong và có response trả về) thì hàm callback truyền vào sẽ được đẩy vào Massage Queue.
Khi biên dịch đoạn code ban đầu thì các dòng lệnh assyn của bạn sẽ được đẩy vào stack, khi các dòng lệnh này chạy từ đầu tới cuối (chúng chạy song song với các tiến trình truy cập I/O nói ở trên) và khi chạy xong câu lệnh cuối cùng rồi thì stack sẽ về rỗng. Một khi stack về rỗng (chạy xong thân code của bạn), vòng lặp Event Loop sẽ được khởi chạy. Mỗi khi Event Loop gặp một messagetrong Message Queue, nó sẽ thực thi hàm callback gắn với message đó bằng cách đẩy các đoạn code trong hàm callback vào stack. Sau khi hàm callback đó thực hiện xong, stack về rỗng, thì Event Loop tiếp tục chạy và lấy message tiếp theo (nếu có) ra và đẩy code của callback vào stack thực thi.

NodeJS xử lý nhiều request cùng lúc như thế nào?

Như giải thích ở phần 2, NodeJS là single thread non-blocking, nên các request tới sau sẽ được thực hiện ngay sau khi thân code của request thứ nhất được thực hiện xong, thân code ở đây là mọi thứ đang có trong call stack và message queue, những callback nằm trong message queue là những callback đã có kết quả trả về, còn những callback thuộc về request thứ nhất mà chưa có kết quả trả về sẽ chưa được đẩy vào message queue.

Câu hỏi đặt ra là request thứ hai tới sẽ được đặt ở đâu để chờ cho request thứ nhất thực hiện xong, request cũng là một event nên nó sẽ được đặt vào message queue, khi các callback được đẩy vào message queue trước request thứ 2 này được thực hiện xong thì request thứ hai này mới được lấy ra vào cho vào call stack để thực hiện. Ở điểm này có thắc mắc là nếu request thứ hai thay đổi biến global của chương trình và request thứ nhất vẫn còn callback (gọi là callback A) được đẩy vào message queue sau request thứ hai và callback A này lại cần dữ liệu của biến global đó thì chẳng phải là callback A đã truy cập sai dữ liệu dẫn tới request thứ nhất trả về kết quả sai sao?

Kết luận rút ra:

  • Các lời gọi I/O là assync (nhiều I/O được thực hiện cùng 1 lúc).
  • Các lời gọi hàm của bạn là sync (Code của bạn chạy tuần tự từ trên xuống dưới).
  • Các hàm callback sẽ chạy sau khi thân code của bạn thực thi xong.
  • Các hàm callback sẽ chạy tuần tự theo thứ tự callback nào được trigger trước sẽ chạy trước chứ không phải các hàm callback được chạy đồng thời.

=> Đây chính là single thread trong NodeJS. Nghĩa là chỉ có một tiến trình được chạy trong code của bạn (Còn những lời gọi I/O assyn là của hệ thống gọi multi thread).

Theo Kipalog

>> Tham khảo thêm: Callback function và Higher-order function trong JavaScript

BizFly Cloud là nhà cung cấp dịch vụ điện toán đám mây với chi phí thấp, được vận hành bởi VCCorp.

BizFly Cloud là một trong 4 doanh nghiệp nòng cốt trong "Chiến dịch thúc đẩy chuyển đổi số bằng công nghệ điện toán đám mây Việt Nam" của Bộ TT&TT; đáp ứng đầy đủ toàn bộ tiêu chí, chỉ tiêu kỹ thuật của nền tảng điện toán đám mây phục vụ Chính phủ điện tử/chính quyền điện tử.

Độc giả quan tâm đến các giải pháp của BizFly Cloud có thể truy cập tại đây.

DÙNG THỬ MIỄN PHÍ và NHẬN ƯU ĐÃI 3 THÁNG tại: Manage.bizflycloud

SHARE