完全搞懂JavaScript執行機制

首先咱們你們都瞭解的是,JavaScript 是一門單線程語言,因此咱們就能夠得出:java

JavaScript 是按照語句順序執行的面試

首先看:瀏覽器

let a = '1'
console.log(a)

let b = '2'
console.log(b)

這個顯然你們都知道結果,依次輸出1,2多線程

然而換一種:架構

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

new Promise(function(resolve) {
    console.log(2)
    for(var i = 0;i< 10;i++){
        i === 10 && resolve()
    }
}).then(function() {
    console.log(3)
})
console.log(4)

這個時候再看代碼的順序執行,輸出1,2, 3, 4。好了放到瀏覽器運行一下,什麼?輸出竟然是 2, 4, 3,1。說好的按順序執行呢?下面就須要去了解一下 JavaScript 的執行機制問題了。框架

單線程

首先JavaScript 是一門單線程的語言,在最新的HTML5 推出的 Web-worker,可是 JavaScript 是一個單線程的語言這一個核心仍是沒有改變。因此,JavaScript 的多線程都是基於單線程模擬出來的。因此牢記 JavaScript 是單線程語言。異步

事件循環

任務分爲兩類:分佈式

  • 同步任務
  • 異步任務

當咱們打開頁面時,頁面的渲染就是一大堆同步任務,而像加載圖片和音頻資源耗時的任務,就是異步任務。時間循環的主要內容就是:函數

  1. 當任務進入執行棧的時候,判斷是同步任務仍是異步任務,若是是同步任務,進入主線程進行執行,異步進入 Event Table 進行註冊函數。
  2. 當指定的事件完成後,Event Table 將這個函數移入到事件隊列
  3. 主線程中的任務執行完畢後,去任務隊列讀取對應的函數,進入主線程執行
  4. 上述的過程不斷重複,也就構成了事件循環

其中js引擎存在一個監控進程,不斷檢查主線程執行棧是否爲空,一旦爲空,就會去時間隊列那檢查有沒有等待被調用的函數。源碼分析

例如:

setTimeout( function() {
    console.log(1)
}, 0)
console.log(2)
  • 首先 setTimeout進入Event Table
  • 執行console.log(2)
  • setTimeout執行的函數進入事件隊列
  • 主線程從事件隊列讀取函數執行

這也就是爲何即便設置setTimeout(fn, 0)函數也不會當即執行的緣由。不過即便主線程爲空,0ms也是達不到的,根據HTML標準,最低是4ms。

setInterval

還有一個與setTimeout相似的函數,對於setInterval來講,是循環執行。對於執行順序來講,setInterval會每隔指定的時間將註冊的函數置入Event Queue,若是前面的任務耗時過久,那麼一樣須要等待。

可是須要注意的一點是,對於setInterval(fn, ms)來講,他並非每過ms執行一次 ,而是每過 ms 會有fn進入任務隊列。也就是說若是setInterval 的回調函數的執行事件若是超過延遲ms,那麼就看不出來事件間隔了。

Promise 和 process.nextTick(callback)

除了廣義的同步任務和異步任務以外,還有對任務更精細的劃分,分爲:

  • macro-task(宏任務):包括總體代碼script、setTimeout、setInterval
  • micro-task(微任務):Promise、process.nextTick

事件循環的順序,決定js代碼的執行順序。進入總體代碼(宏任務)後,開始第一次循環。接着執行全部的微任務。而後再次從宏任務開始,找到其中一個任務隊列執行完畢,再執行全部的微任務。

用一段代碼來講明:

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

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

console.log('4');
  • 這段代碼做爲宏任務,開始第一次循環
  • 先遇到setTimeout,那麼它的回調函數進入到宏任務事件隊列中
  • 遇到Promise,Promise當即執行,輸出2,then任務進入到微任務事件隊列中
  • 下面遇到console,輸出4
  • 第一個宏任務結束,看微任務事件隊列,執行then,輸出3
  • 第一輪循環結束,看宏任務隊列中存在setTimeout的回調函數執行,輸出1
  • 全部結果爲:2,4,3,1

好了瞭解了基本的原理以後,咱們來看一個更復雜的:

console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

不知道你們答案是什麼?接下來咱們來進行分析一下:

第一輪:

  1. 首先整段代碼做爲一個宏任務進入主線程,首先遇到console.log()輸出1
  2. 遇到第一個setTimeout()進入宏任務隊列
  3. 遇到Process.nextTick()進入微任務隊列
  4. 而後遇到Promise,當即執行,輸出7,then被添加到微任務隊列
  5. 遇到第二個setTimeout,進入宏任務隊列
  6. 而後執行兩個微任務
  7. 執行Process.nextTick()輸出6
  8. 執行then,輸出8

這樣第一輪循環就完全結束了,進行第二輪事件循環,也就是第一個setTimeout

  1. 首先遇到console.log(),輸出2
  2. 遇到Process.nextTick(),進入微任務隊列
  3. 遇到Promise當即執行輸出4,then進入微任務隊列
  4. 而後執行第一個微任務,輸出3
  5. 執行then,輸出5

這樣第二輪事件循環就結束了,最後執行第二個setTimeout,第二個setTimeout和上面原理相似,也就不重複說明了。因此最終結果是:1,7,6,8,2,4,3,5,9,11,10,12

 

加Java架構師羣獲取Java工程化、高性能及分佈式、高性能、深刻淺出。高架構。性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點高級進階乾貨的直播免費學習權限 都是大牛帶飛 讓你少走不少的彎路的 羣..號是:855801563 對了 小白勿進 最好是有開發經驗

注:加羣要求

一、具備工做經驗的,面對目前流行的技術不知從何下手,須要突破技術瓶頸的能夠加。

二、在公司待久了,過得很安逸,但跳槽時面試碰壁。須要在短期內進修、跳槽拿高薪的能夠加。

三、若是沒有工做經驗,但基礎很是紮實,對java工做機制,經常使用設計思想,經常使用java開發框架掌握熟練的,能夠加。

四、以爲本身很牛B,通常需求都能搞定。可是所學的知識點沒有系統化,很難在技術領域繼續突破的能夠加。

5.阿里Java高級大牛直播講解知識點,分享知識,多年工做經驗的梳理和總結,帶着你們全面、科學地創建本身的技術體系和技術認知!

相關文章
相關標籤/搜索