首頁 > web前端 > js教程 > 深入理解JavaScript執行機制

深入理解JavaScript執行機制

不言
發布: 2018-08-31 10:12:23
原創
984 人瀏覽過

這篇文章帶給大家的內容是關於深入理解JavaScript執行機制,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

首先我們大家都了解的是,JavaScript 是一門單執行緒語言,所以我們就可以得到:

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. 遇到第一個
  3. setTimeout()

    進入巨集任務佇列

  4. 遇到
  5. Process.nextTick()

    進入微任務佇列

  6. 然後遇到Promise,立即執行,輸出7,then被加入到微任務佇列

  7. 遇到第二個setTimeout,進入巨集任務佇列

  8. 然後執行兩個微任務

  9. 執行Process.nextTick()輸出6

  10. 執行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

相關推薦:

js執行機制實例詳解

JavaScript運行機制之Event Loop

以上是深入理解JavaScript執行機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板