一篇文章搞懂JavaScript運行機制

單線程的JavaScript:

衆所周知JavaScript這門語言是單線程,可是爲何要設計成單線程呢?明明多線程更加有效率。前端

這裏咱們就要從JavaScript的用途來考慮,JavaScript是一門瀏覽器腳本語言,也就是說它須要操做DOM來更改頁面展現,提供用戶良好的上網體驗。這時候單線程的好處就體現出來,不妨想象一下promise

若是JavaScript是多線程的語言,當一個線程正在進行一個DOM的刪除操做,這是另外一個線程出來搞事情,進行這個DOM的修改操做。這種狀況要怎麼處理呢?這樣的場景就會出現一些問題。瀏覽器

因此說JavaScript單線程的設計是從它的用途出發的,而且在之後也會一直堅持單線程的設計。bash


同步、異步:

單線程就像是你們在食堂排隊打飯,若是想打到飯,那就必須等食堂大媽一個一個把排在前面的人的飯打完才能輪到你。可是,若是JavaScript真的是這樣那就糟糕了。不妨想一下,若是我訪問的這個網站有一個超清圖片,加載很慢,難道是要用戶等到圖片徹底加載出來以後才能進行其餘的操做嘛,顯然如今咱們瀏覽網站並不會出現這樣的狀況,那這又是怎麼一回事呢?多線程

那是由於JavaScript的任務分爲同步任務異步任務兩種:異步

  • 同步任務

        同步任務就是進入主線程的任務,必須排隊一個一個按順序的執行。函數

  • 異步任務

        開發者們認識到,像費時的IO操做,接口請求徹底能夠不理他們,將他們暫時掛起,放入事件表(Event Table)中當,主線程中的任務執行完,再來「寵幸」它們。這種暫時掛起的任務就是異步任務。oop


回調函數:

每個異步任務都須要指定一個事件,例如當耗時的IO操做執行完以後,就會將它指定的這個事件添加到任務隊列中,這個事件就是回調函數。學習

因此說咱們常常說的主線程開始執行異步任務了,其實主線程執行的是異步任務的回調函數。網站


Event Loop:


如今來分析一下上圖中的事件執行順序:

  1. 首先任務進入執行棧,會先來判斷這個任務是同步任務仍是異步任務。
  2. 若是是同步任務則進入主線程來執行,若是是異步任務則進入到事件表中註冊函數
  3. 當事件表中的異步事件執行完後會在事件隊列中註冊自身的回調函數
  4. 當主線程的任務執行完後會去事件隊列中檢查是否有須要執行的事件,若是事件隊列中有任務,則進入主線程執行。

上述的過程會不斷的重複執行,這個重複的過程就是咱們一般所說的事件循環機制(Event Loop),看下面代碼:

console.log(1);

document.body.onclick = function () { console.log('2'); }

console.log(3);複製代碼

JavaScript中的給DOM註冊一個點擊事件,這個點擊事件其實就是一種異步任務,由於咱們不知道用戶何時纔會點擊。


如今咱們根據上圖來分析一下這段代碼的執行:

1.首先主線程會將同步的console.log操做放在主線程中執行,

2.首先打印出1,3

3.同時將點擊事件放入到事件表中,當用戶點擊body後,JS會在事件隊列中註冊點擊的回調函數。

4.這時主線程任務執行完畢,去任務隊列中檢查是否有須要執行的任務,這是發現了點擊body的回調函數,JavaScript就會將這個回調函數放在主線程中執行。

5.全部就打印出了1,3,2的結果。


宏任務和微任務

異步任務其實還能夠細分爲宏任務微任務,他們的區別就是執行順序的不一樣,下面咱們就討論一下他們具體的執行順序,在Event Loop中到底有什麼不一樣。

其實異步任務的執行是有兩個執行隊列的,一個是宏任務隊列,一個是微任務隊列,每次執行的時候,首先是去微任務隊列中查看是否有須要執行的任務,而後再去宏任務執行隊列中查看是否有須要執行的事件。


宏任務:總體的script代碼,setTimeout,setInterval、setImmediate

微任務:promise,process.nextTick


咱們如今根據這個流程圖來分析一下具體的執行順序:


  1. 首先執行主線程中的script代碼,也就是執行宏任務
  2. 當宏任務執行完畢後,查看微任務隊列是否有須要執行的事件,若是沒有則繼續查看宏任務,若是有則將微任務隊列中的事件所有執行完畢。
  3. 當微任務清空後,在繼續檢查宏任務隊列是否有可執行的事件,這個循環的過程就是宏任務和微任務的循環過程。


光說不練假把式:

如今咱們來分析一段代碼,看看輸出的順序是否符合上面的流程圖:


  1. 按照上面的流程圖,先檢查同步代碼也就是宏任務代碼進行執行,輸出1,6 ,這裏須要注意(Promise聲明中的代碼是會當即執行的也就是同步代碼)
  2. 檢查微任務事件隊列,這裏面的微任務就是promise.then裏面的代碼,這時輸出7。
  3. 微任務完成以後繼續檢查宏任務,這段代碼裏面的宏任務就是setTimeOut,因此接下來執行宏任務中的代碼,也就是輸出2,4,9,10,
  4. 而後再去執行微任務隊列中的事件,輸出5,11
  5. 在輸出7的時候then方法裏面註冊了setTimeout宏任務事件,在輸出2的時候也一樣註冊了宏任務setTimeout,因此再次檢查宏任務的時候,按順序輸出8,3

因此上述代碼最終的輸出應該是:1,6,7,2,4,9,10,5,11,8,3


結尾

但願你們看完這篇文章可以有收穫,哪裏寫的不對也但願各位大佬多加指點,本文章僅爲記錄前端小白的學習過程,謝謝你們!

相關文章
相關標籤/搜索