[JavaScript]js EventLoop And Async

蘇水軒發表於2019-03-31

What

js事件迴圈

先來看一張圖(這張圖來自於http://www.zcfy.cc/article/node-js-at-scale-understanding-the-node-js-event-loop-risingstack-1652.html)

jsäºä»¶å¾ªç¯

再來看一張圖(這張圖來自於https://www.cnblogs.com/hity-tt/p/6733062.html)

[JavaScript]js EventLoop And Async

從一開始,js有一個執行棧(stack)中存放了該頁面所有的程式碼即該頁面主執行緒,當執行遇到非同步操作(async)時,瀏覽器的webcore模組(如network,timer,domBinding模組等)將其放置到一個幕後執行緒中等待,然後瀏覽器接著執行主執行緒,當幕後執行緒中的程式碼準備好了(如定期器時間到了,請求的響應來了),該執行緒就會把這個函式的回撥放到taskQueue(任務佇列)中等待,當主執行緒的執行棧全部執行完畢時(所有同步操作全部執行完畢),主執行緒對任務對列進行檢測是否有任務要執行,如果有,就把該任務放到執行棧中進行,如果沒有,就保持等待(迴圈檢測)任務到來。

上述的過程,就是事件迴圈(EventLoop)。

Why need EventLoop 

因為web前端端使用者的豐富互動性問題,js這邊採取了單執行緒來應對多個互動產生時的程式操作複雜性問題。由於單執行緒的出現,當同步操作出現長時間等待時就會出現執行緒阻塞,為了解決阻塞,出現了非同步操作(變相多執行緒)來解決執行緒阻塞。

非同步操作就是通過EventLoop的實現的

SimpleLook

事件迴圈三步看

  1.  判斷js程式碼是否為非同步,非同步則放置該程式碼到幕後執行緒,同步則繼續執行
  2.  幕後執行緒中的函式回撥被觸發時將該程式碼推入任務佇列
  3.  同步任務執行完畢後,檢測事件佇列,將任務佇列尾部函式回撥推入執行棧執行
  4.  以上三步迴圈執行,即EventLoop

兩步來看

  1. 執行主執行緒,同步操作,遇到非同步壓入事件表隨後(該程式碼在觸發響應時)壓入佇列
  2. 主執行緒程式碼執行完畢後,無限迴圈(?)檢測非同步事件佇列,有則進入1執行

一句話講清楚

主執行緒執行完畢後,檢測任務佇列有則取出一個插入主執行緒,重複該動作。

任務優先順序

如上圖,任務佇列中的任務分為兩種,對應大型工作場景和微處理場景,即對應的傳說中的Macrotask和Microtask。

what

顧名思義,即多個任務同時出現時的執行優先等級。

Macrotask: setTimeout,setInterval,使用者互動操作/UI渲染(請求回撥執行)etc

Microtask:  Promise,process.nextTick(nodejs) etc

why

當任務佇列中有多個任務時,事件優先順序就需要出現了。

How

大致多工時處理步驟如下四小步(來自網上,感覺該步驟描述有問題)

1. 檢測Macrotask,空3,非空2

2. 從Macrotask佇列中取隊首(在佇列時間最長)的任務進去執行棧中執行(僅僅一個),執行完後進入下一步

3. 檢查Microtask佇列是否為空,若不為空,則4,否則,跳到1(開始新的事件迴圈)

4. 從Microtask佇列中取隊首(在佇列時間最長?)的任務進去事件佇列執行,執行完後,跳到3


簡而言之:同步環境執行 -> 事件迴圈1(microtask queue的All)-> 事件迴圈2(macrotask queue中的一個) -> 事件迴圈1(microtask queue的All)-> 事件迴圈2(macrotask queue中的一個)...


Demo

demo來源於網上,做抽象概念具現化理解用

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

巢狀微任務

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


Last,如果覺得總結的對你的理解有點幫助,請幫忙點個贊吧!!(給您鼓掌h0h)

如果寫的有不對的地方,請您評論留言!謝謝^_^


相關文章