JavaScript是單線程的,具有同步執行模型。單線程意味著一次執行一個命令。同步意味著一次執行一行代碼,即為了代碼的出現而在時間上執行一行代碼。因此,在JavaScript中,一次只發生一件事。
執行上下文
JavaScript引擎與瀏覽器中的其他引擎交互。在JavaScript執行堆棧中,底部有全局上下文,然后當我們調用函數時,JavaScript引擎會為各自的函數創建新的執行上下文。當被調用的函數退出時,它的執行上下文將從堆棧中彈出,然后下一個執行上下文被彈出,以此類推。
例如
function abc(){
console.log('abc');}function xyz(){
abc()
console.log('xyz');}var one = 1;xyz();在上面的代碼中,將創建一個全局執行上下文,并且在此上下文中var one它的價值是1.當調用xyz()調用時,將創建一個新的執行上下文,如果我們在xyz函數中定義了任何變量,這些變量將存儲在xyz()的執行上下文中。在xyz函數中,我們調用abc(),然后創建abc()執行上下文并放到執行堆棧上.現在,當abc()完成其上下文從堆棧中彈出,那么xyz()上下文從堆棧中彈出,然后全局上下文將被彈出.
現在是關于異步回調;異步意味著一次不止一個。
就像執行堆棧一樣,事件隊列..當我們希望得到JavaScript引擎中某些事件的通知時,我們可以監聽該事件,并將該事件放在隊列中。例如,Ajax請求事件或HTTP請求事件。
每當執行堆棧為空時(如上面的代碼示例所示),JavaScript引擎會定期查看事件隊列,并查看是否有任何事件需要通知。例如,隊列中有兩個事件,一個Ajax請求和一個HTTP請求。它還想看看是否有一個函數需要在該事件觸發器上運行.因此,JavaScript引擎會收到關于事件的通知,并知道要在該事件上執行的相應函數.因此JavaScript引擎調用處理程序函數,在示例中,例如AjaxHandler()將被調用,就像調用函數時一樣,它的執行上下文被放置在執行上下文中,現在函數執行完成,事件Ajax請求也從事件隊列中刪除.當AjaxHandler()完成時,執行堆棧是空的,因此引擎再次查看事件隊列,并運行HTTP請求的事件處理程序函數,這是隊列中的下一個。重要的是要記住,只有在執行堆棧為空時才會處理事件隊列。
例如,參見下面解釋Javascript引擎執行堆棧和事件隊列處理的代碼。
function waitfunction() {
var a = 5000 + new Date().getTime();
while (new Date() < a){}
console.log('waitfunction() context will be popped after this line');}function clickHandler() {
console.log('click event handler...'); }document.addEventListener('click', clickHandler);waitfunction(); //a new context for this function is created and placed on the execution stackconsole.log('global context will be popped after this line');和
<html>
<head>
</head>
<body>
<script src="program.js"></script>
</body></html>
現在運行網頁并單擊頁面,然后查看控制臺上的輸出。輸出將是
waitfunction() context will be popped after this lineglobal context will be emptied after this line
click event handler...
JavaScript引擎正在同步運行代碼,正如在執行上下文部分中所解釋的那樣,瀏覽器正在異步地將內容放入事件隊列中。因此,需要很長時間才能完成的函數可以中斷事件處理。JavaScript以這種方式處理瀏覽器中發生的事件,如果需要運行偵聽器,則當執行堆棧為空時,引擎將運行它。事件是按照它們發生的順序來處理的,所以異步部分是關于引擎外部正在發生的事情,即當這些外部事件發生時,引擎應該做什么。
所以JavaScript總是同步的。