MDN之JavaScript-高階(二)【Concurrency model and Event Loop併發模型與事件迴圈】
Web 開發技術> JavaScript >併發模型與事件迴圈
JavaScript 的併發模型基於"事件迴圈"。這個模型與像 C 或者 Java 這種其它語言中的模型截然不同。
執行時概念
下面的內容解釋了一個理論上的模型。現代 JavaScript 引擎著重實現和優化了描述的幾個語義。
視覺化描述
執行棧中的程式碼(同步任務),總是在讀取"任務佇列"(非同步任務)之前執行。
棧
函式呼叫形成了一個棧幀。
function foo(b) {
var a = 10;
return a + b + 11;
}
function bar(x) {
var y = 3;
return foo(x * y);
}
console.log(bar(7));
當呼叫bar
時,建立了第一個幀 ,幀中包含了bar
的引數和區域性變數。當bar
呼叫foo
時,第二個幀就被建立,並被壓到第一個幀之上,幀中包含了foo
的引數和區域性變數。當foo
返回時,最上層的幀就被彈出棧(剩下bar
函式的呼叫幀 )。當bar
返回的時候,棧就空了。
堆
物件被分配在一個堆中,即用以表示一個大部分非結構化的記憶體區域。
佇列
一個 JavaScript 執行時包含了一個待處理的訊息佇列。每一個訊息都有一個為了處理這個訊息相關聯的函式。
在 事件迴圈 時,runtime (執行時)總是從最先進入佇列的一個訊息開始處理佇列中的訊息。正因如此,這個訊息就會被移出佇列,並將其作為輸入引數呼叫與之關聯的函式。為了使用這個函式,呼叫一個函式總是會為其創造一個新的棧幀( stack frame),一如既往。
函式的處理會一直進行直到執行棧再次為空;然後事件迴圈(event loop)將會處理佇列中的下一個訊息(如果還有的話)。
事件迴圈
之所以稱為事件迴圈,是因為它經常被用於類似如下的方式來實現:
while (queue.waitForMessage()) { queue.processNextMessage(); }
如果當前沒有任何訊息queue.waitForMessage
會等待同步訊息到達。
"執行至完成"
每一個訊息完整的執行後,其它訊息才會被執行。當你分析你的程式時,這點提供了一些優秀的特性,包括每當一個函式執行時,它就不能被搶佔,並且在其他程式碼執行之前完全執行(且可以修改此函式操作的資料)。這與C語言不同,例如,如果函式線上程中執行,則可以在任何位置終止然後在另一個執行緒中執行其他程式碼。
這個模型的一個缺點在於當一個訊息需要太長時間才能完成,Web應用無法處理使用者的互動,例如點選或滾動。瀏覽器用“程式需要過長時間執行”的對話方塊來緩解這個問題。一個很好的做法是使訊息處理縮短,如果可能,將一個訊息裁剪成幾個訊息。
新增訊息
在瀏覽器裡,當一個事件出現且有一個事件監聽器被繫結時,訊息會被隨時新增。如果沒有事件監聽器,事件會丟失。所以點選一個附帶點選事件處理函式的元素會新增一個訊息。其它事件亦然。
呼叫 setTimeout
函式會在一個時間段過去後在佇列中新增一個訊息。這個時間段作為函式的第二個引數被傳入。如果佇列中沒有其它訊息,訊息會被馬上處理。但是,如果有其它訊息,setTimeout
訊息必須等待其它訊息處理完。因此第二個引數僅僅表示最少的時間 而非確切的時間。
零延遲
零延遲並不是意味著回撥會立即執行。在零延遲呼叫 setTimeout 時,其並不是過了給定的時間間隔後就馬上執行回撥函式。其等待的時間基於佇列里正在等待的訊息數量。在下面的例子中,"this is just a message" 將會在回撥 (callback) 獲得處理之前輸出到控制檯,這是因為延遲是要求執行時 (runtime) 處理請求所需的最小時間,但不是有所保證的時間。
(function() {
console.log('this is the start');
setTimeout(function cb() {
console.log('this is a msg from call back');
});
console.log('this is just a message');
setTimeout(function cb1() {
console.log('this is a msg from call back1');
}, 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
// "this is a msg from call back"
// "this is a msg from call back1"
多個執行時互相通訊
一個 web worker 或者一個跨域的iframe
都有自己的棧,堆和訊息佇列。兩個不同的執行時只能通過 postMessage
方法進行通訊。如果後者偵聽到message
事件,則此方法會向其他執行時新增訊息。
永不阻塞
事件迴圈模型的一個非常有趣的特性是 JavaScript,與許多其他語言不同,它永不阻塞。 處理 I/O 通常通過事件和回撥來執行,所以當一個應用正等待IndexedDB
查詢返回或者一個 XHR 請求返回時,它仍然可以處理其它事情,如使用者輸入。
例外是存在的,如 alert
或者同步 XHR,但應該儘量避免使用它們。注意,例外的例外也是存在的(但通常是實現錯誤而非其它原因)。
相關文章
- 事件迴圈(event loop)事件OOP
- JavaScript事件迴圈(Event Loop)JavaScript事件OOP
- Javascript 事件迴圈event loopJavaScript事件OOP
- JS事件迴圈Event LoopJS事件OOP
- JS 事件迴圈(Event Loop)JS事件OOP
- 事件迴圈(Event Loop)淺析事件OOP
- nodejs中的事件迴圈 - Event LoopNodeJS事件OOP
- 瀏覽器事件迴圈Event Loop瀏覽器事件OOP
- JavaScript的事件迴圈(Event loop)(附圖)JavaScript事件OOP
- 淺談js的事件迴圈(Event Loop)JS事件OOP
- 談談 Event Loop(事件迴圈)機制OOP事件
- 瀏覽器事件迴圈機制(event loop)瀏覽器事件OOP
- JavaScript深入之事件迴圈機制(event loop)JavaScript事件OOP
- 學習筆記之事件迴圈-Event loop筆記事件OOP
- 一文梳理JavaScript 事件迴圈(Event Loop)JavaScript事件OOP
- 事件迴圈Event loop到底是什麼事件OOP
- JavaScript-事件迴圈-eventLoopJavaScript事件OOP
- 瀏覽器與Node的事件迴圈(Event Loop)有何區別?瀏覽器事件OOP
- 效能優化篇 - js事件迴圈機制(event loop)優化JS事件OOP
- 為什麼要有事件迴圈機制(Event Loop)事件OOP
- 瀏覽器和Node不同的事件迴圈(Event Loop)瀏覽器事件OOP
- 筆試題——JavaScript事件迴圈機制(event loop、macrotask、microtask)筆試JavaScript事件OOPMac
- 理解瀏覽器和nodeJs中的事件迴圈(Event Loop)瀏覽器NodeJS事件OOP
- Event Loop事件迴圈,看完你總會有點收穫!OOP事件
- JavaScript在瀏覽器環境下的事件迴圈(Event Loop)JavaScript瀏覽器事件OOP
- 從event loop到async await來了解事件迴圈機制OOPAI事件
- Js 的事件迴圈(Event Loop)機制以及例項講解JS事件OOP
- JS事件迴圈機制(event loop)之巨集任務/微任務JS事件OOP
- 理解瀏覽器和node.js中的Event loop事件迴圈瀏覽器Node.jsOOP事件
- Node.js Event Loop與瀏覽器 Event Loop(事件環)Node.jsOOP瀏覽器事件
- Node.js中的事件迴圈(Event Loop),計時器(Timers)以及process.nextTick()Node.js事件OOP
- 深入瞭解Flutter的isolate(1) ---- 事件迴圈(event loop)及程式碼執行順序Flutter事件OOP
- JS 事件機制 Event LoopJS事件OOP
- Javascript之Event LoopJavaScriptOOP
- JavaScript與Event LoopJavaScriptOOP
- node中的事件環(Event Loop)事件OOP
- 0165-loop 迴圈OOP
- 淺析 event-loop 事件輪詢OOP事件