Tìm hiểu sâu về Javascript Internals

Feb 02 2023
Hoạt động nội bộ của công cụ Javascript, Vòng lặp sự kiện, API Web, Hàng đợi sự kiện & Cuộc gọi lại. Giới thiệu Bạn đã bao giờ tưởng tượng điều gì sẽ xảy ra nếu không có Javascript chưa? Nếu không có Javascript, bạn sẽ không thể phát trực tuyến phim trên Netflix, mua sắm trên Amazon hoặc xem video trên Youtube.

Hoạt động nội bộ của công cụ Javascript, Vòng lặp sự kiện, API Web, Hàng đợi sự kiện & Cuộc gọi lại.

Javascript

Giới thiệu

Bạn đã bao giờ tưởng tượng điều gì sẽ xảy ra nếu không có Javascript chưa? Nếu không có Javascript, bạn sẽ không thể phát trực tuyến phim trên Netflix, mua sắm trên Amazon hoặc xem video trên Youtube. Hầu như mọi trang web khác đều sử dụng Javascript và làm cho trang web có tính tương tác.

Trước khi Javascript được phát minh, internet chỉ bao gồm các trang HTML đơn giản . HTML & CSS cung cấp cấu trúc và kiểu dáng cho trang web. Nhưng Javascript làm cho trang web động . Bạn có thể nhập thông tin đăng nhập của mình và đăng nhập vào một trang web, thực hiện các giao dịch trực tuyến hoặc chơi trò chơi. Javascript làm cho mọi thứ xảy ra.

Một trong những cuộc khảo sát được thực hiện trên Stack-overflow cho thấy Javascript có số lượng repo cao nhất trên GitHub. Hàng năm, các khung và thư viện mới đang được phát triển bằng Javascript. Ngôn ngữ này đã thực sự biến đổi internet và đơn giản hóa cuộc sống của mọi người.

Lý lịch

Hai năm trước, tôi đang thực hiện một dự án phụ và lần đầu tiên sử dụng Javascript. Là một lập trình viên dày dạn kinh nghiệm từ thập kỷ trước, tôi đã trở nên thông thạo cú pháp và phát triển một lệnh tốt trong vòng ba tháng.

Nhiều tháng trôi qua, trang web mà tôi đã phát triển không hoạt động tốt. Phải mất một thời gian dài để tải và bắt đầu ảnh hưởng đến khách hàng. Vì vậy, tôi đã tìm hiểu sâu để xác định nguyên nhân gốc rễ.

Sau nhiều đêm mất ngủ, cuối cùng tôi cũng kiệt sức và định bỏ cuộc. Tuy nhiên, tôi quyết định thử lần cuối. Cuối cùng, tôi nghĩ đến việc hiểu thấu đáo về cách Javascript hoạt động bên trong. Tôi đã giới thiệu rất nhiều tài nguyên và cuối cùng đã có thể sửa lỗi trong mã của mình trong vòng hai ngày.

Các kỹ sư thường tập trung vào việc xây dựng những thứ mới. Các công cụ và khung mà chúng tôi sử dụng che giấu hầu hết sự phức tạp khỏi chúng tôi. Trong hành trình của mình, chúng ta có xu hướng quên mất cách mọi thứ thực sự hoạt động đằng sau hậu trường. Cần phải biết hoạt động bên trong của bất kỳ công cụ nào mà chúng tôi sử dụng. Hiểu rõ nội tại giúp chúng ta thu hẹp vấn đề và đưa ra giải pháp thực tế.

Trong bài viết này, chúng ta sẽ xem xét điều gì xảy ra ở hậu trường khi Javascript được trình duyệt thực thi. Chúng tôi sẽ xem xét công cụ Javascript và hiểu nó đang hoạt động. Chúng ta sẽ thấy các thành phần khác nhau liên quan đến thời gian chạy Javascript và hiểu rõ hơn về Vòng lặp sự kiện & hàng đợi Sự kiện. Mặc dù những thuật ngữ này có thể mới đối với bạn, nhưng bài viết sẽ cố gắng đơn giản hóa từng thuật ngữ. Hãy bắt đầu với nhiệm vụ nắm vững các nội dung bên trong của Javascript.

Javascript

Javascript là một ngôn ngữ thông dịch cấp cao. Không giống như C hay C++, chúng ta không phải biên dịch mã Javascript rồi chạy nó. Trước tiên, mã Javascript được biên dịch thành mã byte và sau đó được dịch sang mã cấp độ máy.

Các ngôn ngữ cao cấp như Java, C, C++, C# hỗ trợ đa luồng. Các ngôn ngữ này cho phép lập trình viên tạo nhiều luồng và chạy các tác vụ trong các luồng khác nhau. Do đó, chúng ta có thể sinh ra các tác vụ trên nhiều CPU và sử dụng hiệu quả các tài nguyên máy tính.

Ngược lại, Javascript là luồng đơn. Điều này có nghĩa là chỉ có một luồng thực thi duy nhất tại bất kỳ thời điểm nào. Trong trường hợp bạn thực hiện một thời gian sử dụng chức năng javascript, trang web sẽ đóng băng. Bạn có thể đã thấy lỗi Trang web không phản hồi nhiều lần trong khi duyệt.

Lỗi trang không phản hồi

Hãy làm một bản demo nhanh và xem nó hoạt động như thế nào. Tôi đã mở Twitter và tôi có thể nhấp vào các tab riêng lẻ như Trending, News, Sportsv.v.

Khám phá phần của Twitter

Bây giờ, trong developerbảng điều khiển của tôi, tôi đang viết một hàm chứa vòng lặp vô hạn . Dưới đây là định nghĩa của hàm.

function infiniteLoop() {
    for(i=0; i < 100000; ) {
    }
}
infiniteLoop()

GIF bên dưới cho thấy rằng tôi không thể thay đổi tab thành Sportshoặc Entertaintment.

Màn hình bị đóng băng do vòng lặp vô hạn

Vì hàm chứa một vòng lặp vô hạn nên nó sẽ tiếp tục thực thi không ngừng. Khi tôi chọn các tab khác nhau, trình duyệt sẽ xử lý sự clickkiện và theo đó hiển thị nội dung của tab được nhấp. Tôi không thể chọn bất kỳ tab nào khác vì Javascript đang chạy chức năng này infiniteLoop.

Như đã thấy trong ví dụ trên, chúng ta chỉ có thể chạy một chức năng tại một thời điểm trong Javascript. Bây giờ, bạn có thể đặt câu hỏi, làm cách nào Trình duyệt có thể tải các trang web phức tạp trong một phần giây.

Chúng ta sẽ hiểu trong các phần tiếp theo Javascript thực hiện điều kỳ diệu này ở hậu trường như thế nào. Bây giờ chúng ta hãy hiểu cốt lõi của Javascript, tức là Javascript Engine.

Công cụ Javascript

Mỗi trình duyệt cung cấp Công cụ Javascript của riêng mình. Ví dụ: - Chrome sử dụng công cụ V8, trong khi Firefox sử dụng SpiderMonkey Engine. Engine chịu trách nhiệm thực thi từng dòng mã Javascript. Sau đây là danh sách các trình duyệt phổ biến và công cụ Javascript của chúng.

Công cụ Javascript bao gồm Heap và Stack . Hãy hiểu hoạt động của hai yếu tố này.

đống

Heap chịu trách nhiệm lưu trữ các định nghĩa hàm, các biến đối tượng . Nó chứa tất cả các đối tượng đang được mã tham chiếu.

Các đối tượng được tạo động trong heap. Hoạt động tương tự như java. Bất cứ khi nào đối tượng được tham chiếu bởi một hàm, nó sẽ được tìm nạp từ heap.

function bar() {
    var player = { "firstName": "Shubhman",
                   "lastName": "Gill"
                 }
    var teams = ["India", "NewZealand"]
    i =0
    a = 10
    console.log("In the function bar")
}

bar()

      
                
Objects on Heap

Cây rơm

Ngăn xếp là cấu trúc dữ liệu Nhập sau xuất trước (LIFO). Mỗi khi một hàm được chạy, các biến cục bộ của hàm đó được đẩy lên ngăn xếp. Khi chức năng hoàn thành thực thi, nó sẽ bị xóa khỏi ngăn xếp. Nếu một hàm gọi một hàm mới trong quá trình thực thi, thì hàm mới đó cũng được đẩy vào ngăn xếp. Công cụ chạy chức năng ở trên cùng của ngăn xếp.

Các chức năng ở dưới cùng của ngăn xếp sẽ tiếp tục thực hiện sau khi tất cả các chức năng ở trên nó trở lại. Trong trường hợp một chức năng đưa ra lỗi, dấu vết ngăn xếp được in trên tệp console. Dấu vết ngăn xếp bao gồm chuỗi hàm được gọi khi ném ngoại lệ. Hãy xem xét một ví dụ.

Tôi sẽ chạy đoạn mã trên trong trình duyệt của mình. Và thêm một điểm dừng trong chức năng city.

Thực thi Javascript trong trình duyệt

Như đã thấy ở trên, khi thực thi tập lệnh, nó đã dừng tại điểm dừng trong hàm city. Ở bên phải, trong Call Stackphần, bạn có thể thấy citychức năng ở trên cùng.

Bao Call Stackgồm tất cả các chức năng được gọi cho đến nay. Và chức năng gần đây nhất được đặt ở trên cùng. Khi chức năng hoàn thành thực thi, nó sẽ bị xóa khỏi ngăn xếp.

Bạn có thể thấy trong hình ảnh bên dưới, chức năng cityđã bị xóa và việc thực thi tiếp tục cho chức năng streetở dòng số 9.

Như đã thấy ở trên, Javascript chỉ có thể thực thi một chức năng tại một thời điểm và chỉ có một luồng thực thi. Bạn không thể chạy đồng thời nhiều chức năng trong ngăn xếp thực thi.

Giả sử chúng ta có một chức năng thực hiện cuộc HTTPgọi và nhận dữ liệu từ một trang web khác. Điều gì sẽ xảy ra nếu máy chủ chậm và chức năng mất nhiều thời gian để thực thi? Chúng tôi sẽ không thể chạy bất kỳ chức năng nào khác và trải nghiệm người dùng sẽ rất kém.

Điều này có xảy ra trong thế giới thực không? Không, các trang web phức tạp như Facebook.com , Twitter.com , Amazon.com , v.v. chạy hàng tấn mã Javascript mà không ảnh hưởng đến trải nghiệm người dùng. Để hiểu cách thực hiện, chúng ta sẽ đi sâu vào thời gian chạy Javascript.

thời gian chạy Javascript

Javascript cung cấp cho chúng tôi nhiều API khác nhau để lấy dữ liệu từ máy chủ, thao tác DOM hoặc thực thi các chức năng bằng cách sử dụng bộ đếm thời gian. Do tính chất đơn luồng của Javascript, nó có thể tạm dừng kết xuất trang web, do đó dẫn đến hiệu suất kém. Tuy nhiên, thời gian chạy Javascript đảm bảo rằng điều này không xảy ra. Hãy hiểu điều này với một ví dụ.

Bạn có thể đoán đầu ra của đoạn mã dưới đây sẽ là gì: -

Sẽ getSubTitlesđược in sau khi in getActors? Và sau khi gọi setTimeout, mã sẽ dừng lại?

Sau đây là đầu ra được tạo bằng cách chạy đoạn mã trên: -

đầu ra mã

Như đã thấy ở trên, cái getSubTitlesđược in đầu tiên và 4 giây sau, getActorsđược in. Mặc dù, chức năng setTimeoutđã chờ trong 4 giây, nhưng nó vẫn thực thi chức năng tiếp theo getSubTitlestrước đó và mã không chờ. Bây giờ chúng ta hãy hiểu những gì đã xảy ra đằng sau hậu trường.

API web

Bạn sẽ không tìm thấy việc triển khai các chức năng như setTimeouthoặc fetchbất kỳ đâu trong mã nguồn Javascript. Các chức năng này được cung cấp bởi trình duyệt và không phải là một phần của Javascript. Việc triển khai sẽ khác nhau đối với mọi trình duyệt.

Trình duyệt cung cấp các chức năng để thực hiện các cuộc gọi mạng, thao tác DOM, xử lý âm thanh, video, v.v. Các chức năng này tạo thành các API Web. Việc triển khai nội bộ rất phức tạp và chủ yếu bao gồm mã C++. Các API cung cấp cho chúng tôi một giao diện có thể được gọi từ mã Javascript.

Khi bạn gọi một chức năng chẳng hạn như setTimeout, Javascript sẽ hiểu rằng đây là API Web. Sau đó, nó ủy quyền việc thực thi chức năng này cho bộ chứa API Web. Chức năng setTimeoutđược thực hiện trong một luồng riêng biệt. Nó không được thực thi trong luồng thực thi của Javascript. Đây là lý do tại sao mã được viết sau khi setTimeoutđược thực thi trước khi hàm gọi lại được truyền cho nó.

Tương tự, do các cuộc gọi mạng mất nhiều thời gian nên các chức năng như fetchđược thực thi trong một luồng riêng biệt. Các API Web của trình duyệt ẩn hầu hết sự phức tạp khỏi chúng tôi. Ví dụ: - việc triển khai nội bộ fetchsẽ bao gồm hình thành kết nối TCP, gửi dữ liệu và phân tích cú pháp. Ngoài ra, nó cũng sẽ sử dụng tài nguyên máy tính một cách hiệu quả bằng cách lên lịch thực hiện trên luồng thích hợp.

Ảnh gif dưới đây cho thấy điều gì sẽ xảy ra khi bạn thực thi các chức năng API Web như setTimeout.

Javascript trong hành động

Như đã thấy ở trên, một khi setTimeouthàm được gọi, nó sẽ gửi hàm gọi lại getActorstới các API Web. Trong vùng chứa API Web, chức năng này setTimeoutđợi trong bốn giây. Trong khi đó, chức năng tiếp theo getSubTitlesđược gọi.

Bây giờ, bạn có thể hỏi chức năng gọi lại được getActorsthực thi như thế nào sau khi bộ đếm thời gian kết thúc. Chúng ta sẽ xem trong phần tiếp theo, cách gọi lại được thực thi sau khi API Web trả về.

hàng đợi sự kiện

Javascript phụ thuộc rất nhiều vào các chức năng gọi lại. Các chức năng gọi lại được thực thi khi một sự kiện xảy ra. Ví dụ: - Sau khi nhận được phản hồi từ máy chủ, chúng tôi cần in jsonbiểu diễn. Hoặc thực hiện một chức năng sau 4 giây trễ.

Thời gian chạy Javascript bao gồm một thành phần được gọi là Hàng đợi sự kiện hoặc Hàng đợi gọi lại. Đó là một hàng đợi các tin nhắn và sử dụng thứ tự Nhập trước Xuất trước (FIFO). Các thông báo không là gì ngoài các chức năng gọi lại được lên lịch để thực thi.

Hãy lấy một ví dụ để hiểu điều này. Trong đoạn mã dưới đây, chúng tôi đang đăng ký chức năng gọi lại bất cứ khi nào có sự kiện nhấp vào nút. Khi người dùng nhấp vào nút, chức năng onClicksẽ được gọi. Chức năng này gọi chức năng timersau khi trì hoãn 2 giây.

Hoạt hình trên cho thấy những gì xảy ra đằng sau hậu trường. Chúng tôi có hai API Web tại đây — a) $.onvà b) setTimeout. API đầu tiên chờ một sự kiện nhấp chuột diễn ra. Sau khi nhận được sự kiện nhấp chuột, nó sẽ thực thi chức năng onClick. API thứ hai setTimeOutđược gọi sau đó bởi onClickhàm.

Mỗi lần, người dùng nhấp vào nút, chức năng onClicksẽ được đẩy vào Hàng đợi sự kiện. Tương tự, sau khi bộ đếm thời gian kết thúc, chức năng timersẽ được đẩy vào hàng đợi. Vì vậy, hàng đợi sự kiện được tạo thành từ tất cả các chức năng gọi lại phải được thực thi. Trong trường hợp này, onClickvà timerchức năng.

Vòng lặp sự kiện

Các chức năng gọi lại tiếp EventQueuecận ngăn xếp thực thi của Javascript thông qua Vòng lặp sự kiện. Vòng lặp Sự kiện liên tục kiểm tra xem Ngăn xếp Javascript hiện tại có trống không. Nếu nó thấy rằng ngăn xếp trống, thì nó sẽ chọn thông báo đầu tiên từ Hàng đợi sự kiện và đẩy vào ngăn xếp.

Lấy ví dụ ở phần trước, ngay khi Vòng lặp sự kiện nhận thấy rằng ngăn xếp trống, nó sẽ lấy thông báo tiếp theo từ hàng đợi gọi lại và thực thi nó.

Dưới đây là mã giả thô cho hoạt động của vòng lặp sự kiện: -

Bạn có thể coi Vòng lặp sự kiện là một vòng lặp vô hạn liên tục giám sát ngăn xếp thực thi. Trong trường hợp ngăn xếp trống, nó sẽ nhận một thông báo (chức năng) từ Hàng đợi sự kiện và thực hiện nó.

Sơ đồ bên dưới hiển thị biểu diễn cấp cao của thời gian chạy Javascript.

thời gian chạy Javascript

Chúng tôi đã xem xét các thành phần khác nhau trong thời gian chạy Javascript. Bây giờ bạn sẽ có một ý tưởng tốt hơn về cách thức hoạt động ẩn của Javascript. Hãy tóm tắt những gì chúng ta đã học được cho đến nay.

Bản tóm tắt

  • Javascript là một luồng đơn và thực hiện một chức năng tại một thời điểm.
  • Nó tận dụng các API Web để thực thi các tác vụ phức tạp trong nền.
  • Khi quá trình thực thi API Web hoàn tất, nó sẽ gửi các chức năng gọi lại đến hàng đợi Sự kiện.
  • Vòng lặp sự kiện liên tục kiểm tra xem ngăn xếp Javascript có trống không. Trong trường hợp ngăn xếp trống, nó chọn chức năng gọi lại từ hàng đợi Sự kiện và chạy nó.