JS的運行機制到底是什麼?

先給大家來段代碼,看大家能否知道輸出的結果

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')
    })
})

瞭解js的同步和異步

先要明白js是單線程語言,所有的js多線程都是用單線程模擬出來的

JS的運行機制到底是什麼?_單線程

導圖要表達的內容用文字來表述的話:

  • 同步和異步任務分別進入不同的執行"場所",同步的進入主線程,異步的進入Event Table並註冊函數。
  • 當指定的事情完成時,Event Table會將這個函數移入Event Queue。
  • 主線程內的任務執行完畢為空,會去Event Queue讀取對應的函數,進入主線程執行。
  • 上述過程會不斷重複,也就是常説的Event Loop(事件循環)。

我們不禁要問了,那怎麼知道主線程執行棧為空啊?js引擎存在monitoring process進程,會持續不斷的檢查主線程執行棧是否為空,一旦為空,就會去Event Queue那裏檢查是否有等待被調用的函數。

用我自己的話説:

打開一個js代碼,先執行同步任務,遇到異步任務,把異步任務放到隊列中,等同步任務執行完之後,在執行異步任務,非常好理解

其實這個不太準確,同步和異步是廣義範圍內的,如果要在精細一點,就分為宏任務和微任務,後面會講

那些是同步?那些是異步?

異步:setTimeout(),setInterval(),Promise,process.nextTick(callback) 等

同步:剩下的基本都是同步的

宏任務和微任務

  • macro-task(宏任務):包括整體代碼script,setTimeout,setInterval,dom事件,ajax請求
  • micro-task(微任務):Promise,process.nextTick,asycn/await

JS的運行機制到底是什麼?_單線程_02

結合圖片和上面的文字可以看出

  • 整個script就是一個宏任務,所以先把script放入到宏任務中
  • 遇到setTimeout,setInterval,把它們也放到宏任務中。遇到Promise,process.nextTick,把它們放到微任務中
  • 先執行宏任務,執行完成,執行微任務,完成之後再執行宏任務,知道把所有的任務都執行完成

接下來,看上面代碼的輸出結果

安裝宏任務和微任務的執行順序,一點一點來

  1. 把js中所有是宏任務的代碼都放到宏隊列中,所有的微任務都放到微隊列中,能直接輸出的直接輸出
  • 先輸出1
  • 遇到setTimeout(),把它放到宏任務,有兩個,先標記為set1,set2
  • 遇到process.nextTick,把執行的函數放到微任務
  • 遇到promise,只把.then的函數放到微任務,所以輸出7
  • 第一步完成後輸出1,7
  1. 然後執行微任務
  • 有兩個微任務,要注意先後順序,從上到下
  • 執行process.nextTick,輸出6
  • 執行promise.then,輸出8
  • 第二步完成後輸出6,8
  1. 微任務執行完,在執行宏任務,有兩個宏任務,set1,set2
  • 接下來和上面的操作是一樣的,先執行宏任務set1
  • 輸出2,4
  • 把promise.then和process.nextTick放入微任務
  • 執行微任務,輸出3,5
  • 第三步完成後輸出2,4,3,5
  1. 再執行宏任務set2
  • 輸出9,11
  • 把promise.then和process.nextTick放入微任務
  • 執行微任務,輸出10,12
  • 第四步完成後輸出9,11,10,12

最終結果1,7,6,8,2,4,3,5,9,11,10,12

不知道各位小夥伴答案對不對呢?