1. 程式人生 > >JS中的異步以及事件輪詢機制

JS中的異步以及事件輪詢機制

alt sha ack web 網絡請求 HA click 特性 隊列

轉載出處:http://www.cnblogs.com/heshan1992/p/6650593.html

一、JS為何是單線程的?

JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。那麽,為什麽JavaScript不能有多個線程呢?這樣能提高效率啊。(在JAVA和c#中的異步均是通過多線程實現的,沒有循環隊列一說,直接在子線程中完成相關的操作)

JavaScript的單線程,與它的用途有關。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復雜的同步問題。比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為準?

所以,為了避免復雜性,從一誕生,JavaScript就是單線程,這已經成了這門語言的核心特征,將來也不會改變。

為了利用多核CPU的計算能力,HTML5提出Web Worker標準,允許JavaScript腳本創建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個新標準並沒有改變JavaScript單線程的本質。

二、JS是單線程的,那麽他是如何是實現異步操作的?

JS的異步是通過回調函數實現的,即通過任務隊列,在主線程執行完當前的任務棧(所有的同步操作),主線程空閑後輪詢任務隊列,並將任務隊列中的任務(回調函數)取出來執行。"回調函數"(callback),就是那些會被主線程掛起來的代碼。異步任務必須指定回調函數,當主線程開始執行異步任務,就是執行對應的回調函數。

雖然JS是單線程的但是瀏覽器的內核是多線程的,在瀏覽器的內核中不同的異步操作由不同的瀏覽器內核模塊調度執行,異步操作會將相關回調添加到任務隊列中。而不同的異步操作添加到任務隊列的時機也不同,如 onclick, setTimeout, ajax 處理的方式都不同,這些異步操作是由瀏覽器內核的 webcore 來執行的,webcore 包含上圖中的3種 webAPI,分別是 DOM Binding、network、timer模塊。

  onclick 由瀏覽器內核的 DOM Binding 模塊來處理,當事件觸發的時候,回調函數會立即添加到任務隊列中。
 setTimeout 會由瀏覽器內核的 timer 模塊來進行延時處理,當時間到達的時候,才會將回調函數添加到任務隊列中。  ajax 則會由瀏覽器內核的 network 模塊來處理,在網絡請求完成返回之後,才將回調添加到任務隊列中。

JS中的異步運行機制如下:

1 2 3 4 (1)所有同步任務都在主線程上執行,形成一個執行棧(execution context stack)。 (2)主線程之外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。 (3)一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,於是結束等待狀態,進入執行棧,開始執行。 (4)主線程不斷重復上面的第三步。

下圖就是主線程和任務隊列的示意圖 事件輪詢

技術分享圖片 技術分享圖片

只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重復。(該過程又稱之為事件輪詢)

三、JS種事件隊列的優先級

在JS中ES6 中新增的任務隊列(promise)是在事件循環之上的,事件循環每次 tick 後會查看 ES6 的任務隊列中是否有任務要執行,也就是 ES6 的任務隊列比事件循環中的任務(事件)隊列優先級更高。

如 Promise 就使用了 ES6 的任務隊列特性。也即在執行完任務棧後首先執行的是任務隊列中的promise任務。其他的上面常見的異步操作加入隊列的時間沒有相應的優先級。

技術分享圖片
setTimeout(function(){console.log(‘111‘)},0);
new Promise(function(resolve,reject){
   console.log("2222");//此處還沒有執行異步操作,執行異步操作及執行回調函數,在promise中即then中的回調
  resolve();
}).then(function(){console.log(‘3333‘)})
console.log("44444");
//輸出
 2222
 44444//上面的兩個輸出屬於同步操作
 3333//promise加入到隊列的優先級高於setTimeout
 111
技術分享圖片

同時在嵌套異步操作中,會將嵌套的異步加入到下次的任務隊列中,以此類推(如嵌套的promise)。

技術分享圖片
new Promise(function(resolve,reject){
  resolve();
}).then(function(){
    console.log("111");
    return new Promise(function(resolve,reject){
   resolve();
})
}).then(function(){ console.log("222");})

new Promise( function(resolve,reject){
    resolve();
}).then(function(){ console.log("33333");})
//輸出
111 33333 222
技術分享圖片

JS中的異步以及事件輪詢機制