JavaScript執行機制

_zhongmeizhi_發表於2019-04-01

Javascript是一種單執行緒開發語言。理解Javascript的執行機制是日常編碼必須要掌握的技能。

為什麼是單執行緒?

JavaScript的主要用途是與使用者互動,以及操作DOM。這決定了它只能是單執行緒,否則會帶來很複雜的同步問題。

  • 假設:如果JavaScript支援多執行緒,一個執行緒在某個DOM節點上新增內容,另外一個執行緒刪除了這個節點,那麼瀏覽器該以哪個執行緒為準呢?

單執行緒的缺點

單執行緒就意味著容易發生執行緒等待資源,cpu空閒,而其他任務一直等待的問題。

什麼是Event Loop(事件迴圈)

為了協調事件、使用者互動、指令碼、UI 渲染和網路處理等行為,防止主執行緒阻塞。於是Javascript設計者將所有任務分為兩種,一種是同步任務,一種是非同步任務

  • 同步任務指的是,在主執行緒上排隊執行的任務
    • 同步任務只有前一個任務執行完畢,才能執行下一個任務。
    • 同步任務都在主執行緒上執行,形成一個執行棧
      • 每次執行棧執行的程式碼就是一個巨集任務
  • 非同步任務指的是,不進入主執行緒,而進入任務佇列的任務。
    • 只要指定過回撥函式,這些事件發生時就會進入"任務佇列"(比如滑鼠點選...等)
    • 一旦執行棧中的所有同步任務執行完畢,系統就會讀取“任務佇列”。
    • 任務佇列是一個先進先出的資料結構,排在前面的事件,優先被主執行緒讀取。

"主執行緒"從"任務佇列"中讀取事件,這個過程是迴圈不斷的,所以整個的這種執行機制又稱為Event Loop(事件迴圈)。

巨集任務和微任務

根據規範:每個任務都有一個任務源(task source),源自同一個任務源的 task 必須放到同一個任務佇列,從不同源來的則被新增到不同佇列,所以有了巨集任務(macro)task和微任務(micro)task。

瀏覽器為了能夠使得JS內部(macro)task與DOM任務能夠有序的執行,會在一個task執行結束後,在下一個(macro)task 執行開始前,對頁面進行重新渲染,

每次執行完一個巨集任務之後,會去檢查是否存在微任務;如果有,則執行微任務直至清空微任務佇列,如果在微任務執行期間微任務佇列加入了新的微任務,會將新的微任務加入佇列尾部,之後也會被執行。

根據上述總結流程為:

![流程圖]

附(巨集/微任務清單):

  • 巨集任務(macro)task主要有: script(整體程式碼)、setTimeout、setInterval、I/O、UI互動事件、postMessage、MessageChannel、setImmediate(Node.js 環境)
  • 微任務(micro)task主要有: Promise.then、MutaionObserver、process.nextTick(Node.js 環境)
  • requestAnimationFrame 既不屬於巨集任務, 也不屬於微任務

目前巨集任務和微任務在各瀏覽器執行都有差異,最後提議promise為微任務

例項分析

    setTimeout(function(){
        console.log('1');
    });

    new Promise(function(resolve){
        console.log('2');
        resolve();
    }).then(function(){
        console.log('3');
    });

    console.log('4');

複製程式碼

以上案例會輸出 2 4 3 1

結果解析:

  1. JavaScript執行主執行緒任務:輸出 2 4
    • 附:Promise構造器內部是同步任務
  2. 執行微任務佇列:輸入 3
  3. 第一個巨集任務結束,進入setTimeout回撥:輸出 1

End

持續更新中 來Github 點顆⭐吧

返回主頁

相關文章