js單線程以及異步隊列執行

一、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)所有同步任務都在主線程上執行,形成一個執行棧(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  111
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章