Đầu tiên, bạn đưa ra một giả định không chính xác: JavaScript hiện đại được biên soạn. Các động cơ như V8, Spidermonkey và Nitro Compile JS nguồn vào mã máy gốc của nền tảng máy chủ.
Ngay cả trong các động cơ cũ hơn, JavaScript không được giải thích. Họ chuyển đổi mã nguồn thành mã byte, mà máy ảo của động cơ thực thi.
Đây thực sự là cách mọi thứ trong ngôn ngữ Java và .NET hoạt động: Khi bạn "biên dịch" ứng dụng của mình, bạn thực sự chuyển đổi mã nguồn thành mã byte của nền tảng, mã byte và CIL tương ứng. Sau đó, trong thời gian chạy, một trình biên dịch JIT biên dịch mã byte vào mã máy.
Chỉ các động cơ JS rất cũ và đơn giản thực sự diễn giải mã nguồn JavaScript, bởi vì việc giải thích là rất chậm.
Vậy tổng hợp JS hoạt động như thế nào? Trong giai đoạn đầu tiên, văn bản nguồn được chuyển thành một cây cú pháp trừu tượng [AST], một cấu trúc dữ liệu đại diện cho mã của bạn theo định dạng mà máy móc có thể xử lý. Về mặt khái niệm, điều này giống như cách văn bản HTML được chuyển đổi thành đại diện DOM của nó, đó là những gì mã của bạn thực sự hoạt động.
Để tạo AST, động cơ phải đối phó với đầu vào của các byte thô. Điều này thường được thực hiện bởi một máy phân tích từ vựng. Lexer không thực sự đọc tệp "từng dòng"; Thay vào đó, nó đọc byte-by-byte, sử dụng các quy tắc của cú pháp ngôn ngữ để chuyển đổi văn bản nguồn thành mã thông báo. Lexer sau đó chuyển luồng mã thông báo cho trình phân tích cú pháp, đó là những gì thực sự xây dựng AST. Trình phân tích cú pháp xác minh rằng các mã thông báo tạo thành một chuỗi hợp lệ.
Bây giờ bạn có thể thấy rõ lý do tại sao một lỗi cú pháp ngăn mã của bạn hoạt động. Nếu các ký tự bất ngờ xuất hiện trong văn bản nguồn của bạn, động cơ không thể tạo ra AST hoàn chỉnh và nó không thể chuyển sang giai đoạn tiếp theo.
Khi động cơ có AST:
- Một trình thông dịch có thể chỉ cần bắt đầu thực hiện các hướng dẫn trực tiếp từ AST. Điều này rất chậm.
- Việc triển khai JS VM sử dụng AST để tạo mã byte, sau đó bắt đầu thực thi mã byte.
- Trình biên dịch sử dụng AST để tạo mã máy, CPU thực thi.
Vì vậy, bây giờ bạn có thể thấy rằng tối thiểu, việc thực thi JS xảy ra theo hai giai đoạn.
Tuy nhiên, các giai đoạn thực hiện thực sự không có tác động đến lý do tại sao ví dụ của bạn hoạt động. Nó hoạt động vì các quy tắc xác định cách đánh giá và thực thi các chương trình JavaScript. Các quy tắc có thể dễ dàng được viết theo cách mà ví dụ của bạn sẽ không hoạt động, không có tác động đến cách chính động cơ thực sự diễn giải/biên dịch mã nguồn.
Cụ thể, JavaScript có một tính năng thường được gọi là nâng cao. Để hiểu được nâng cao, bạn phải hiểu sự khác biệt giữa khai báo hàm và biểu thức chức năng.
Đơn giản, một khai báo chức năng là khi bạn khai báo một chức năng mới sẽ được gọi ở nơi khác:
function foo[] {
}
Một biểu thức chức năng là khi bạn sử dụng từ khóa function
ở bất kỳ nơi nào mong đợi một biểu thức, chẳng hạn như gán biến hoặc trong một đối số:
var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
JavaScript bắt buộc các khai báo chức năng [loại đầu tiên] được gán cho các tên biến ở đầu bối cảnh thực thi, bất kể khai báo ở đâu trong văn bản nguồn [của ngữ cảnh]. Bối cảnh thực thi gần như tương đương với phạm vi - theo thuật ngữ đơn giản, mã bên trong một hàm hoặc phần trên cùng của tập lệnh của bạn nếu không bên trong một hàm.regardless of where the declaration appears in source text [of the context]. An execution context is roughly equatable to scope – in plain terms, the code inside a function, or the very top of your script if not inside a function.
Điều này có thể dẫn đến hành vi rất tò mò:
var foo = function[] { console.log['bar']; };
function foo[] { console.log['baz']; }
foo[];
Bạn mong đợi điều gì sẽ được đăng nhập vào bảng điều khiển? Nếu bạn chỉ cần đọc mã tuyến tính, bạn có thể nghĩ baz
. Tuy nhiên, nó thực sự sẽ đăng nhập bar
, bởi vì tuyên bố của foo
được nâng lên trên biểu thức gán cho foo
.
Vì vậy, để kết luận:
- Mã nguồn JS không bao giờ là "đọc" từng dòng.
- Mã nguồn JS thực sự được biên dịch [theo nghĩa thực sự của từ] trong các trình duyệt hiện đại.
- Động cơ biên dịch mã trong nhiều lần vượt qua.
- Hành vi là ví dụ của bạn là sản phẩm phụ của các quy tắc của ngôn ngữ JavaScript, chứ không phải cách nó được biên dịch hoặc giải thích.
Gần đây tôi đã thảo luận với một vài Devs Devs về - cách JS phân bổ bộ nhớ và cách một kịch bản được phân tích cú pháp và thực thi. Đây là một trong nhiều chủ đề quan trọng nhất [hầu hết] không bao giờ là một phần trong sự nghiệp học tập của chúng tôi, không ai cần biết để viết một chương trình JS. Nhưng các chủ đề như thế này là rất quan trọng đối với những nhà phát triển tò mò ngoài kia, những người nghiêm túc với mọi thứ. Tôi đã chọn viết về chủ đề này bởi vì tôi thấy nó khá mơ hồ và mọi người có xu hướng so sánh mọi thứ, đặc biệt là những người quen thuộc với các ngôn ngữ lập trình khác như PHP, C ++, Java, v.v. Một thời gian để tiêu hóa một số khía cạnh quan trọng của JS ban đầu, như làm thế nào JavaScript bị chủ đề đơn có thể không chặn và đồng thời?HOW JavaScript being single-threaded can be non-blocking and concurrent?
Bây giờ trước khi chúng ta bắt đầu lặn sâu, hãy làm rõ khái niệm cơ bản và sự khác biệt giữa động cơ JavaScript và môi trường thời gian chạy JavaScript. - Mặt khác, môi trường thời gian chạy JavaScript, vì tên này có trách nhiệm tạo ra một hệ sinh thái với các cơ sở, dịch vụ và hỗ trợ [như mảng, chức năng, thư viện lõi, v.v.] được yêu cầu cho các hướng dẫn thực thi để chạy thành công .
- JavaScript engine is a program that is used to parse a given script and convert it to machine-executable instructions.
- On the other hand JavaScript run-time environment, as the name implies is responsible for creating an ecosystem with facilities, services, and supports [like array, functions, core libraries, etc.] that are required for the executable instructions to run successfully.
Mô hình chức năng
Hầu như tất cả các trình duyệt web đều có một công cụ JavaScript. Những cái phổ biến nhất là V8 trong Google Chrome và Node.js, Spider Monkey of Mozilla, IE, Chakra, v.v. Mặc dù tất cả các nhà cung cấp trình duyệt này đã triển khai JS khác nhau, nhưng dưới mui xe, tất cả đều theo cùng một mô hình cũ.
Hình: 1Gọi ngăn xếp, API web, vòng lặp sự kiện, hàng đợi nhiệm vụ, hàng đợi kết xuất, v.v ... Tất cả chúng ta đều nghe thấy các thuật ngữ buzz này trong công việc hàng ngày của chúng ta. Nói chung, tất cả đều làm việc cùng nhau để giải thích và thực hiện các khối mã đồng bộ và không đồng bộ mà chúng ta viết mỗi ngày. Chúng ta hãy nhìn sâu vào mô hình và cố gắng hiểu những gì họ làm và quan trọng nhất là cách thức.
Nhiệm vụ đồng bộ
Có nghĩa là gì? Giả sử chúng tôi có 2 dòng mã dòng 1 theo sau là Line-2. Đồng bộ có nghĩa là Line-2 không thể bắt đầu chạy cho đến khi Line-1 thực hiện xong.Say we have 2 lines of codes Line-1 followed by Line-2. Synchronous means Line-2 can not start running until the Line-1 has finished executing.
JavaScript là một luồng đơn, có nghĩa là chỉ có một câu lệnh được thực thi tại một thời điểm. Khi JS Engine xử lý từng dòng tập lệnh của chúng tôi, nó sử dụng ngăn xếp gọi đơn này để theo dõi các mã được cho là chạy theo thứ tự tương ứng của chúng. Giống như những gì một ngăn xếp làm, một cấu trúc dữ liệu ghi lại các dòng hướng dẫn thực thi và thực hiện chúng theo cách LIFO. Vì vậy, hãy nói rằng nếu động cơ bước vào function foo[]{
, nó sẽ đẩy foo [] vào ngăn xếp và khi việc thực hiện foo [] ________ 9 đã vượt qua foo [] được bật ra khỏi ngăn chặn., which means only one statement is executed at a time. As the JS engine processes our script line by line, it uses this single Call-Stack to keep track of codes that are supposed to
run in their respective order. Like what a stack does, a data structure that records lines of executable instructions and executes them in a LIFO manner. So say if the engine steps into a function foo[]{
it PUSH-es foo[] into the stack and when the execution of foo[]return; }
is over foo[] is POP-ped out of the call-stack.
Bài tập 1: Vì vậy, từ sơ đồ trên cho thấy cách thực hiện theo dòng điển hình xảy ra. Khi tập lệnh của ba câu lệnh console.log [] được ném vào JS - Bước 1:
0is được đẩy vào ngăn xếp cuộc gọi và được thực thi, sau khi thực hiện với thực thi, thì nó được bật ra khỏi ngăn xếp. Bây giờ ngăn xếp trống và sẵn sàng cho bất kỳ hướng dẫn tiếp theo nào được thực thi. So from the above diagram shows how a typical line by line execution happens. When the script of three console.log[] statements is thrown at JS — var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
Step 1: The
var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
0is pushed into the call stack and executed, once done with execution, it is then popped out of the stack. Now the stack is empty and ready for any next
instruction to be executed.Step 2:
var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
1is pushed and the same thing repeats until - Step 3: is executed and there is nothing left to push and execute.Hãy tham gia vào bài tập tiếp theo của chúng tôi:
Bài tập 2: Vậy những gì đang xảy ra ở đây như sau: So what's happening here is as follows:
Bước 1: Stack cuộc gọi được thúc đẩy với câu lệnh thực thi đầu tiên của tập lệnh của chúng tôi, cuộc gọi chức năng
2. Trong khi thực hiện thông qua phạm vi của hàm var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2 Động cơ của chúng tôi gặp một chức năng khác, hãy gọi var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4 Call stack is
pushed with the first executable statement of our script the var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2 function call. While executing through the scope of the function var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2 our engine encounters another function call var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4Bước 2: Do đó, chức năng gọi
4 được đẩy vào ngăn xếp cuộc gọi và động cơ bắt đầu thực thi ________ 14function cơ thể [Lưu ý: Hàm var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2is vẫn chưa hoàn thành], một lần nữa, có một chức năng khác gọi var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8 bên trong var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
9 Hence the function call var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4 is pushed into the call stack and the engine starts executing var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4function’s body [Note: The function var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2is still not finished], again, there’s another function call
var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8 inside var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
9 body.Bước 3: Tương tự như vậy, chức năng gọi
8 được đẩy vào ngăn xếp cuộc gọi và động cơ bắt đầu xử lý định nghĩa chức năng var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8. Trong khi các chức năng var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4 và var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2Still sống trong ngăn xếp chờ đến lượt của họ [người kế nhiệm hoàn thành việc thực hiện của họ] tương ứng. Likewise the function call var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8 is pushed into the call stack and the engine starts processing var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8 function definition. While the functions var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4 and var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2still living in the stack waiting for their turn [successor to finish their execution] respectively.Bước 4: Vì vậy, khi động cơ gặp phải câu lệnh
4 trong định nghĩa hàm của var foo = function[] { console.log['bar']; };
function foo[] { console.log['baz']; }
foo[];
8, thì đó là sự kết thúc của var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8. Do đó var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8 được bật ra khỏi ngăn xếp cuộc gọi vì nó đã hoàn thành thực thi. Tại thời điểm này, động cơ đã trở lại trong việc thực hiện các dịch vụ của var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4. So when the engine encounters a var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
var foo = function[] { console.log['bar']; };
function foo[] { console.log['baz']; }
foo[];
4 statement within the function definition of var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8 , well that’s the end
of var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8 . Hence var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
8 is popped out of the call stack as it has finished execution. At this point, the engine is back at executing var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4 ‘s offerings.Bước 5: Cũng như động cơ gặp phải câu lệnh
4, hàm var foo = function[] { console.log['bar']; };
function foo[] { console.log['baz']; }
foo[];
4 được bật ra và động cơ bắt đầu thực thi var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2. Bây giờ, không có tuyên bố hoàn trả nào trong phạm vi của var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2 để động cơ thực hiện cơ thể cho đến khi kết thúc phạm vi và bật var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2 ra khỏi ngăn xếp ở bước 6. Well as the engine encounters a var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
var foo = function[] { console.log['bar']; };
function foo[] { console.log['baz']; }
foo[];
4 statement, the function var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
4 is popped out and the engine starts executing var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2 . Now there’s no return statement within the var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2 ‘s scope so the engine executes its body until the end of scope and pops var foo = function[] { };
$.get['/something', function[] { /* callback */ }];
2 out of the stack at Step
6.Đó là cách mà một kịch bản của các tác vụ đồng bộ được xử lý bởi trình duyệt của chúng tôi mà không liên quan đến bất cứ điều gì khác ngoài ngăn xếp cuộc gọi huyền thoại của Hồi giáo. Nhưng mọi thứ trở nên phức tạp một chút khi động cơ JS gặp một nhiệm vụ không đồng bộ.
Nhiệm vụ không đồng bộ
Có nghĩa là gì có nghĩa là gì? Không giống như đồng bộ, không đồng bộ là một hành vi. Giả sử nếu chúng ta có hai dòng mã dòng 1 theo sau là Line-2. Line-1 là một hướng dẫn tốn thời gian. Vì vậy, Line-1 bắt đầu thực hiện hướng dẫn của nó trong nền [như quy trình daemon], cho phép Line-2 bắt đầu thực thi mà không phải chờ Line-1 kết thúc.Unlike synchronous, asynchronous is a behavior. Say if we have two lines of code Line-1 followed by Line-2. Line-1 is a time-consuming instruction. So Line-1 starts executing its instruction in the background [like a daemon process], allowing Line-2 to start executing without having to wait for Line-1 to finish.
Chúng ta cần hành vi này khi mọi thứ chậm. Thực thi đồng bộ mã có vẻ đơn giản nhưng có thể chậm. Các nhiệm vụ như xử lý hình ảnh có thể chậm, các hoạt động tệp có thể thực sự chậm, việc yêu cầu mạng và chờ phản hồi chắc chắn là chậm, tạo ra các tính toán lớn như hơn 100 triệu lần lặp lại cho vòng lặp có phần chậm. Vì vậy, những điều chậm chạp như vậy trong Stack Stack dẫn đến việc chặn chặn. Khi ngăn xếp cuộc gọi bị chặn, trình duyệt sẽ ngăn người dùng ngắt và các câu lệnh mã khác thực thi cho đến khi câu lệnh chặn được thực thi và ngăn xếp cuộc gọi được giải phóng. Do đó, các cuộc gọi lại không đồng bộ được sử dụng để xử lý các tình huống như vậy.slow. Tasks like image processing can be slow, file operations can be really slow, making a network request and waiting for response is definitely slow, making huge calculations like over a 100 million for-loop iteration is somewhat slow. So such slow things in Call stack results in “Blocking”. When the call stack is blocked, the browser prevents the user’s interrupts and other code statements from executing until the blocking statement is executed and the call stack is freed. Hence Asynchronous callbacks are used to handle such situations.
Ví dụ: Vâng, hàm function
4 có lẽ là cách đơn giản nhất và dễ nhất để chứng minh hành vi không đồng bộ.: Well the function function
4 is probably the simplest and
easiest way to demonstrate asynchronous behavior.
Bài tập 3: Nếu chúng ta theo dõi ngăn xếp cuộc gọi, chúng ta có thể thấy: Ở bước 1: Như thường lệ function
5 bị đẩy vào ngăn xếp trước và được thực hiện sau đó xuất hiện khi thực hiện. không thể được thực thi [hoặc được đẩy vào ngăn xếp] ngay lập tức vì nó có nghĩa là thực thi trong một thời gian trong tương lai [nghĩa là sau tối thiểu 2 giây]. Vì vậy, nó biến mất ngay bây giờ [sẽ giải thích sau đó nơi nó biến mất] .step 3: Dòng tiếp theo tự nhiên function
8 được đẩy vào ngăn xếp, được thực thi và được bật ra ngay lập tức. Đột nhiên function
9 được tìm thấy bị đẩy vào ngăn xếp sau 2 giây vì function
4 đã hết thời gian. Sau đó, nó được thực hiện và sau khi hoàn thành được bật ra khỏi ngăn xếp ở bước 6: Stack lại trống. If we trace the call-stack we can see:
at Step 1: As usual function
5 gets pushed into the stack first and is executed then popped out when done.
Step 2: function
4 gets pushed into the stack, but Note- function
7 cannot be executed [or pushed to the stack] immediately because it is
meant to execute in some future time [i.e. after a minimum of 2 seconds]. So it disappears for now [will explain later where it disappears to].
Step 3: Naturally next line function
8 is pushed into the stack, gets executed, and is popped out immediately.
Step 4: Call stack is empty and waiting.
Step 5: Suddenly function
9 is found pushed into the stack after 2 seconds as function
4 has timed out. It
is then executed and once done is popped out of the Stack at Step 6: Stack is empty again.
Do đó, nó chứng minh, mặc dù JavaScript là một luồng, chúng ta có thể đạt được sự đồng thời thông qua việc xử lý không đồng bộ các nhiệm vụ.
Bây giờ chúng ta còn lại ba câu hỏi quan trọng: Câu 1. Điều gì đã xảy ra với ____34? Câu hỏi 2. Nó đã trở lại từ đâu? Và câu hỏi 3. Nó đã xảy ra như thế nào?
Question 1. WHAT happened to thefunction
4 ?
Question 2. From WHERE did it came back?
And
Question 3. HOW did it happen?
Vì vậy, đây là nơi vòng lặp sự kiện, hàng đợi gọi lại và API Web [trong trình duyệt] bắt đầu. Hãy giới thiệu từng phần trên và trả lời 3 câu hỏi trên thông qua sơ đồ tiếp theo của chúng tôi.Event Loop, Callback Queue, and Web APIs [in browser] kicks in. Let's introduce each of the above pieces and answer the above 3 questions through our next diagram.
Hình: 3.2 Trình diễn cuối cùng của toàn bộ mô hìnhBài tập 4: Hãy nhảy ngay vào Let's jump right into
Bước 2: Tại thời điểm này baz
2 bị đẩy vào ngăn xếp cuộc gọi. Như chúng ta có thể thấy nó có hai thành phần A baz
3 và baz
4. Bây giờ function
4 không phải là một phần của bất kỳ động cơ JavaScript nào, trên thực tế, nó có một API web được bao gồm trong môi trường trình duyệt như một tính năng bổ sung. At
this point baz
2 gets pushed into the call stack. As we can see it has two components a baz
3 and a baz
4 . Now function
4 is NOT a part of any JavaScript engine, it’s in fact, a Web API included in the browser environment as an extra feature.
Bước 3: Vì vậy, API Web Trình duyệt chịu trách nhiệm của baz
3 được cung cấp và kích hoạt bộ đếm thời gian của 2000 ms để lại tuyên bố function
4 đã hoàn thành công việc của mình, vì vậy nó đã bật ra khỏi ngăn xếp. [Câu 1 được trả lời]So the browser Web API takes the responsibility of the baz
3 provided and fires up the timer of 2000 ms leaving behind function
4 statement which has done its job,
so it popped out of the stack. [ Question 1 is answered]
Bước 4: Dòng tiếp theo trong tập lệnh của chúng tôi baz
8 được đẩy vào ngăn xếp và bật ra sau khi thực hiện. The next line in our script baz
8 is pushed into the stack and popped out after its execution.
Bước 5: Bây giờ chúng tôi có một baz
9in Webapis sẽ được kích hoạt sau 2000 ms. Nhưng Webapis trực tiếp không thể đẩy mọi thứ ngẫu nhiên vào ngăn xếp cuộc gọi, bởi vì nó có thể tạo ra một sự gián đoạn cho một số mã khác được thực thi bởi công cụ JavaScript tại thời điểm đó. Vì vậy, thay vào đó, baz
3 được chèn vào hàng đợi/hàng đợi gọi lại sau khi hẹn giờ của bar
1is. Webapi hiện đang trống và được giải phóng Now we have a baz
9in the WebAPIs which is going to get triggered after 2000 ms. But WebAPIs directly can not PUSH things randomly into the call-stack, because it might create an interrupt to some other code being
executed by the JavaScript engine at that moment. So instead the baz
3 is inserted into the Callback Queue/Task Queue after the timer of bar
1is over. WebAPI is now empty and freed
Bước 6: Vòng lặp sự kiện-Nó chịu trách nhiệm lấy phần tử đầu tiên từ hàng đợi gọi lại/nhiệm vụ và đẩy nó vào ngăn xếp cuộc gọi chỉ khi ngăn xếp hoặc miễn phí, vì vậy tại thời điểm này của phương trình của chúng tôi, ngăn xếp cuộc gọi trống rỗng. Event Loop — it is responsible for taking out the first element from the Callback/Task Queue and PUSH it into the Call-Stack only when the stack is empty or free, so at this point of our equation, the Call-Stack is empty.
Bước 7: Vì vậy, baz
3 được đẩy vào ngăn xếp cuộc gọi [vì nó miễn phí và trống rỗng] từ Callback/Taskqueue bởi vòng lặp sự kiện và baz
3 được thực thi. [Câu 2 được trả lời] So baz
3 is pushed into the Call-Stack [as it was free and empty] from the Callback/Task queue by the Event Loop, and baz
3 is executed. [Question 2 is answered]
Bước 8: Vì vậy, một tuyên bố thực thi khác function
7 được tìm thấy bên trong phạm vi của baz
3, do đó function
7 được đẩy vào ngăn chặn cuộc gọiSo another executable statement function
7 is found inside the baz
3 ‘s scope, therefore function
7 is pushed into the Call-Stack
Bước 9: Sau khi function
7 được thực thi, sau đó nó được bật ra khỏi ngăn xếp gọi và động cơ JavaScript quay lại để hoàn thành việc thực hiện cơ thể còn lại của baz
3. Mà khi được thực hiện, baz
3 được bật ra khỏi đoạn gọi. Phần cuối của câu chuyện [trả lời các cách thức của người Viking, tức là câu hỏi 3]. Once function
7 is executed, it is then popped out of the Call-Stack, and the JavaScript engine comes back to finish executing the baz
3 ‘s remaining body. Which when done, baz
3 is popped out of the Call-Stack. The End of the story [Answers the “HOW”, i.e Question 3].
Chà, đây là một cuộc biểu tình rất dễ dàng, nhưng mọi thứ trở nên lộn xộn và phức tạp trong các tình huống như khi có nhiều lần được xếp hàng - kết quả khác nhau so với những gì mà thường được mong đợi [đây là một chủ đề thú vị khác để thảo luận]. Tôi không biết làm thế nào chính xác tôi có thể chứng minh chủ đề này nhưng có rất nhiều thứ có thể được xây dựng hoặc giải thích tốt hơn, nhưng tôi đã vội vàng và nó đã quá dài. Hy vọng nó sẽ giúp ai đó.