前言
js是一種單執行緒的語言,所以它通過event loop機制實作了對異步任務和多執行緒,
首先你要對堆疊、佇列的資料結構有一定的了解,其次還要會Promise才能看懂今天的內容,
一、在了解event loop前,我們首先要知道一些基礎知識
宏任務:script全部代碼、setTimeout、setInterval、setImmediate、I/O、UI Rendering,
微任務:Process.nextTick(Node 獨有)、Promise 等,
event loop大體由三個部分組成:呼叫堆疊(call stack)、訊息佇列(Message Queue)、微任務佇列(Microtask Queue),這三個部分在event loop中非常重要,
二、event loop的運行機制
下面通過一些例子舉例說明什么是event loop
1.只有宏任務時
代碼如下(示例):
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
console.log(6)
fn1()
console.log(7)
}
fn2();
首先event loop會從全域堆疊一行一行執行,首先遇到 fn2(),此時fn2()進入呼叫堆疊,
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
console.log(6)
fn1()
console.log(7)
}
=>fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| |
| fn2() | -------------------------
------------------------
隨后進入到fn2()中,遇到 console.log ,將其壓入堆疊,
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
=> console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| console.log(6) |
| fn2() | -------------------------
------------------------
執行console.log(6)并彈出,
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
=> console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| |
| fn2() | -------------------------
------------------------
//輸出 6
遇到 fn1(),此時fn1()進入呼叫堆疊,
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
console.log(6)
=> fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| fn1() |
| fn2() | -------------------------
------------------------
//輸出 6
進入fn1(),遇到console.log,將其壓入堆疊并執行并彈出(跟上面一樣),輸出多了一個 5 ,
function fn1(){
=> console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| fn1() |
| fn2() | -------------------------
------------------------
//輸出 6 5
隨后遇到setTimeout,將它的內容壓入訊息佇列,訊息佇列的內容會在呼叫堆疊清空后再開始執行!
function fn1(){
console.log(5)
=> setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| fn1() | console.log(4)
| fn2() | -------------------------
------------------------
//輸出 6 5
此時fn1執行完,從呼叫堆疊彈出,執行回到fn2,遇到console.log,將其壓入呼叫堆疊,
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
console.log(6)
fn1()
=> console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| console.log(7) | console.log(4)
| fn2() | -------------------------
------------------------
//輸出 6 5
console.log執行后,輸出7,隨后fn2也執行完畢從呼叫堆疊彈出,此時呼叫堆疊為空,
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| | console.log(4)
| | -------------------------
------------------------
//輸出 6 5 7
最后執行訊息佇列中的內容,
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| |
| | -------------------------
------------------------
//輸出 6 5 7 4
總結:首先將按順序執行,遇到函式壓入堆疊,遇到宏任務壓入訊息佇列,堆疊清空后執行訊息佇列,
2.即有宏任務,又有微任務
代碼如下(示例):
let p = new Promise(function(resolve){
console.log(3)
resolve()
})
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
p.then(funtion(){
console.log(1)
})
console.log(6)
fn1()
console.log(7)
}
fn2();
注意這一段代碼與之前的有什么不同?
我們在最開始宣告了一個Promise物件,并在fn2中呼叫了它的then方法,
此時在fn2呼叫前唯一的不同就是,我們在最開始創建Promise物件的時候執行了一個console.log并輸出3,然后將這個Promise的狀態更改為successed,
let p = new Promise(function(resolve){
console.log(3)
resolve()
})
function fn1(){
console.log(5)
setTimeout(function(){
console.log(4)
},0)
}
function fn2(){
p.then(funtion(){
console.log(1)
})
console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| |
| | -------------------------
------------------------
微任務佇列
-------------------------
-------------------------
//輸出 3
接下來執行到fn2中的then方法時,會將根據promise的狀態將resolve的內容壓入微任務佇列(此處我們不考慮構造Promise物件時異步呼叫resolve的情況,此處如果不理解可以查看與Promise原理有關的資料),
let p = new Promise(function(resolve){
console.log(3)
resolve()
})
function fn1(){
console.log(5)
setTimmout(function(){
console.log(4)
},0)
}
function fn2(){
p.then(funtion(){
console.log(1)
})
console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| |
| fn2() | -------------------------
------------------------
微任務佇列
-------------------------
consolo.log(1)
-------------------------
//輸出 3
接下來的執行都與上面一致,我們直接來到fn2執行完畢,
let p = new Promise(function(resolve){
console.log(3)
resolve()
})
function fn1(){
console.log(5)
setTimmout(function(){
console.log(4)
},0)
}
function fn2(){
p.then(funtion(){
console.log(1)
})
console.log(6)
fn1()
console.log(7)
}
fn2();
呼叫堆疊 訊息佇列
| | -------------------------
| | consolo.log(4)
| | -------------------------
------------------------
微任務佇列
-------------------------
consolo.log(1)
-------------------------
//輸出 3 6 5 7
此時我們發現,呼叫堆疊被清空了,此時微任務佇列要優先于訊息佇列執行,也就是說,先執行console.log(1)再執行console.log(4),
最終的師叔也就是3 6 5 7 1 4,
總結:遇到微任務會加入微任務佇列,當呼叫堆疊清空時,如果訊息佇列和微任務佇列都有內容,先執行微任務佇列,再執行訊息佇列,
以上內容為個人學習過event loop后的個人理解,如有不準確的地方請及時指出,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/256816.html
標籤:其他
上一篇:HTML+CSS實作的動態背景圖
