javascript運行機制

事件循環

  • JS分爲同步任務和異步任務
  • 同步任務都在主線程上執行,形成一個執行棧
  • 主線程之外,事件觸發線程管理着一個任務隊列,只要異步任務有了運行結果,就在任務隊列之中放置一個事件。
  • 一旦執行棧中的所有同步任務執行完畢(此時JS引擎空閒),系統就會讀取任務隊列,將可運行的異步任務添加到可執行棧中,開始執行。
    事件循環機機制
    事件循環機制進一步補充:
    事件循環機制

上圖大致描述就是:

1)主線程運行時會產生執行棧
2)棧中的代碼調用某些api時,它們會在事件隊列中添加各種事件(當滿足觸發條件後,如ajax請求完畢)
3)而棧中的代碼執行完畢,就會讀取事件隊列中的事件,去執行那些回調
4)如此循環
5)注意,總是要等待棧中的代碼執行完畢後纔會去讀取事件隊列中的事件

關於定時器思考:
問:當調用setTimeout後, 是如何等待特定時間後才添加到事件隊列中的?是JS引擎檢測的麼?

答:它是由定時器線程控制的, 計時完成後就會將特定的事件推入事件隊列中。

宏任務與微任務

除了廣義的同步任務和異步任務,進一步,JS中又分爲兩種任務類型:macrotask和microtask,在ECMAScript中,microtask稱爲jobs,macrotask可稱爲task。

它們的定義?區別?簡單點可以按如下理解:

macrotask(又稱之爲宏任務),可以理解是每次執行棧執行的代碼就是一個宏任務(包括每次從事件隊列中獲取一個事件回調並放到執行棧中執行)\

  • 每一個task會從頭到尾將這個任務執行完畢,不會執行其它
  • 瀏覽器爲了能夠使得JS內部task與DOM任務能夠有序的執行,會在一個task執行結束後,在下一個 task 執行開始前,對頁面進行重新渲染 (task->渲染->task->…)

microtask(又稱爲微任務),可以理解是在當前 task 執行結束後立即執行的任務

  • 也就是說,在當前task任務後,下一個task之前,在渲染之前
  • 所以它的響應速度相比setTimeout(setTimeout是task)會更快,因爲無需等渲染
  • 也就是說,在某一個macrotask執行完後,就會將在它執行期間產生的所有microtask都執行完畢(在渲染前)

分別是什麼樣的場景會形成macrotask和microtask呢?

  • macrotask:主代碼塊,setTimeout,setInterval等(可以看到,事件隊列中的每一個事件都是一個macrotask)
  • microtask:Promise,process.nextTick等

再根據線程來理解下:

  • macrotask中的事件都是放在一個事件隊列中的,而這個隊列由事件觸發線程維護
  • microtask中的所有微任務都是添加到微任務隊列(Job Queues)中,等待當前macrotask執行完畢後執行,而這個隊列由JS引擎線程維護 (感覺上是)

所以,總結下運行機制:

  • 執行一個宏任務(棧中沒有就從事件隊列中獲取)
  • 執行過程中如果遇到微任務,就將它添加到微任務的任務隊列中
  • 宏任務執行完畢後,立即執行當前微任務隊列中的所有微任務(依次執行)
  • 當前宏任務執行完畢,開始檢查渲染,然後GUI線程接管渲染
  • 渲染完畢後,JS線程繼續接管,開始下一個宏任務(從事件隊列中獲取)
    js運行機制

看一段示例代碼:

setTimeout(function () {
    console.log("setTimeout---5");
})
new Promise(function (resolve, reject) {
    console.log("Promise---1");
    resolve("success");
}).then(function (e) {
    console.log(e+"---3");
    console.log("then---4");
})
console.log("console---2")

運行結果爲:

Promise---1
console---2
success---3
then---4
setTimeout---5

再看一段示例代碼:

console.log("1");
setTimeout(function(){
    console.log("2");
    new Promise(function(resolve,reject){
        console.log("3");
        resolve("4");
    }).then(function(e){
        console.log(e);
        console.log("5")
    })
})
new Promise(function(resolve,reject){
    console.log("6");
    reject("7");
}).then(function(e){
    console.log(e);
    console.log("don't run here");
},function(e){
    console.log(e);
    console.log("8");
})
setTimeout(function(){
    console.log("9");
    new Promise(function(resolve,reject){
        console.log("10");
        resolve("11");
    }).then(function(e){
        console.log(e);
        console.log("12")
    })
})

運行結果爲:

1
6
7
8
2
3
4
5
9
10
11
12
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章