Javascript
有一個基於Event Loop
事件循環的併發模型;javascript
下面講解一個理論模型,講解現代瀏覽器javascript 引擎實現機制和講解一下描述的一些語義詞;java
可視模型表明:web
函數調用造成了一個棧幀瀏覽器
function foo(b) {
var a = 10;
return a + b + 11;
}
function bar(x) {
var y = 3;
return foo(x * y);
}
console.log(bar(7)); // 返回 42
複製代碼
簡單介紹下函數調用的過程:併發
當調用bar(7)
時,創建了第一個 stack frame
是 bar
(包含參數7
和本地變量);當 bar
調用 foo
時候,創建了第二個 stack frame
是 foo
(包含參數 3* 7
和本地變量), 而且放置在 bar
的上方
,也就是棧的頂部
了。app
當 foo(21)
執行完畢 返回 42 的時候,foo
這個棧幀會被移除掉,只剩下了 bar(7)
;而後再執行 bar
, 有返回後,整個棧都是空的。函數
對象都被關聯在Heap
裏面,即用於表示一大塊非結構化的內存區域。oop
一個 Javascript
運行時使用一系列待處理消息的消息隊列。每一個消息關聯一個函數去處理消息。ui
在事件循環的一些時刻,運行時從最早進入隊列的消息開始處理隊列中的消息
。這樣作的話,消息從隊列中被移除,並做爲輸入參數調用與之關聯的函數。就如上面所說,調用一個函數老是爲其創造一個的棧幀。this
函數的執行一直會持續到 stack
變成 空的。而後若是消息隊列還有消息的話,事件循環將會執行消息隊列的下一個消息。
之因此稱爲事件循環,是由於他的執行實現的方式以下:
while (queue.waitForMessage()) {
queue.processNextMessage();
}
複製代碼
若是當前沒有消息,queue.waitForMessage()
會同步等待消息的到達。
每個消息都被徹底執行結束後,纔回去執行下一個消息的處理。
這爲程序的分析提供了一些優秀的特性,包括:不管什麼時候執行一個函數,都不會被搶佔,而且會在其餘代碼執行以前就已經被徹底執行(而且能夠修改函數操做的數量)。
這個和 C語言不太同樣,好比,若是一個函數運行在一個線程中,一些時候,會被執行系統因在別的線程執行其餘代碼中斷。
這個模型的缺點時,當一個消息須要太長時間去執行的時候,web用戶就沒法處理一些,好比click
, srcoll
的交互。瀏覽器會彈出一個 「a script is taking too long to run」
這樣的對話框來緩解這個狀況。一個好的解決辦法就是,縮短消息處理的時間,或者把一個消息分割成多個消息
。
在web瀏覽器裏面,只要有事件發生而且有監聽器綁定的時候,必定會增長一個消息
。若是沒有監聽器,則事件消失。因此,一個元素的點擊而且帶有點擊事件處理,必定會增長一個消息到消息隊列中去。
setTimeout 函數有兩個參數:添加隊列的消息
和 時間(默認 0 )
,這個時間值表明着這個消息被添加到消息隊列最小的延遲時間。若是消息隊列中,沒有別的消息,這個消息會在延遲時間達到以後,立馬會被處理。若是消息隊列有別的消息,setTimeout
這個消息必定要等到別的消息被處理完後才能執行。因爲這個緣由,因此第二參數代表了最小的時間間隔
,而非確切的時間
。
舉例說明,當第二個參數的時間過時後,setTimeout 不會被執行:
const s = new Date().getSeconds();
setTimeout(function() {
// prints out "2", meaning that the callback is not called immediately after 500 milliseconds.
console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
}, 500);
while(true) {
if(new Date().getSeconds() - s >= 2) {
console.log("Good, looped for 2 seconds");
break;
}
}
複製代碼
零延遲不是真實表明着在0毫秒後回調函數會執行。
setTimeout 的零延遲,在給定的時間間隔後不會執行回調函數。
是否執行決定於消息隊列中的等待任務的數量。
舉例說明:
(function() {
console.log('this is the start');
setTimeout(function cb() {
console.log('Callback 1: this is a msg from call back');
});
console.log('this is just a message');
setTimeout(function cb1() {
console.log('Callback 2: this is a msg from call back');
}, 0);
console.log('this is the end');
})();
// "this is the start"
// "this is just a message"
// "this is the end"
// 當前函數 note that function return, which is undefined, happens here
// "Callback 1: this is a msg from call back"
// "Callback 2: this is a msg from call back"
複製代碼
'this is just a message' 雖然在回調以後,卻會在回調以前輸出到打印臺上,這是由於這個零延遲只是處理請求的最小延遲,並不是一個保證的精確的時間。
通常地,setTimeout 須要等待全部其餘消息隊列的代碼執行完以後,纔會執行,即時你設置了特殊的時間間隔。