javascript從誕生之日起就是一門 單線程的 非阻塞的 腳本語言,單線程意味着,javascript代碼在執行的任何時候,都只有一個主線程來處理所有的任務,非阻塞靠的就是 event loop(事件循環),本文就講解下事件循環。
event loop它最主要是分三部分:主線程、宏隊列(macrotask)、微隊列(microtask)
js的任務隊列分為同步任務和異步任務,所有的同步任務都是在主線程里執行的,異步任務可能會在macrotask或者microtask里面
主線程
就是訪問到的script標簽里面包含的內容,或者是直接訪問某一個js文件的時候,里面的可以在當前作用域直接執行的所有內容(執行的方法,new出來的對象等)
宏隊列(macrotask)
setTimeout、setInterval、setImmediate、I/O、UI rendering
微隊列(microtask)
promise.then、process.nextTick
執行順序
1、先執行主線程
2、遇到宏隊列(macrotask)放到宏隊列(macrotask)
3、遇到微隊列(microtask)放到微隊列(microtask)
4、主線程執行完畢
5、執行微隊列(microtask),微隊列(microtask)執行完畢
6、執行一次宏隊列(macrotask)中的一個任務,執行完畢
7、執行微隊列(microtask),執行完畢
8、依次循環。。。
看下面代碼練習練習
console.log(1)
process.nextTick(() => {
console.log(8)
setTimeout(() => {
console.log(9)
})
})
setTimeout(() => {
console.log(2)
new Promise(() => {
console.log(11)
})
})
requestIdleCallback(() => {
console.log(7)
})
// 特殊說明: new Promise()屬於主線程任務
let promise = new Promise((resolve,reject) => {
setTimeout(() => {
console.log(10)
})
resolve()
// 這個console也屬於主線程任務
console.log(4)
})
fn()
console.log(3)
promise.then(() => {
console.log(12)
})
function fn(){
console.log(6)
}
結果是1、4、6、3、12、8、2、11、10、9、7
這個寫法可以囊括80%以上的event loop循環機制的場景了,下面開始梳理具體的運行機制。
js是從上到下執行的,所以上來先打印的是 1 ,繼續往下走;
遇見了process.nextTick,因為它屬於微隊列(microtask),並且當前主線程的代碼還沒有執行完畢,所以它被展示扔到了微隊列里,暫時不打印;
這個時候又遇到了setTimeout,setTimeout是屬於宏隊列(macrotask);
requestIdleCallback,這里也是不立即執行的,它也不屬於任何隊列,這里不做詳細解釋;
promise在實例化的時候,這里的setTimeout繼續被丟到了宏隊列(macrotask)中,並執行了成功的方法,在有promise.then的調用的時候就會去出發,但這里不做打印,接着發現了console,這里直接打印 4 );
fn函數直接調用,直接打印 6 ;
console,直接打印 3 ;
promise.then因為它屬於微隊列,但是它在promise實例化的時候被調用了,所以它會在微隊列的最前面執行;
到這里主線程里面就沒有任何可以執行到東西了,下面開始走微隊列(microtask):
由於promise.then被提前調用了,所以它會先執行,打印 12 ;
微隊列(microtask)里面還有一個,就是上面的process.nextTick,執行它,打印 8 ,這個時候發現它有一個setTimeout,放到宏隊列(macrotask);
到這里微隊列就走完了,下面開始走宏隊列(macrotask):
最外面的setTimeout在一開始的時候被放了進去,所以先執行它,打印 2 ,發現它里面有promise被實例化,直接執行,打印 11 ;
下一個要走的就是promise里面的setTimeout,打印 10 ;
還剩最后一個setTimeout,就是process.nextTick里面的,打印 9 ;
到這里主線程、宏隊列(macrotask)、微隊列(microtask)就全都跑完了,在全部跑完的時候,requestIdleCallback才會執行,打印 7 ;
requesIdleCallback會在當前瀏覽器空閑時期去依次執行,在整個過程當中你可能添加了多個requestIdleCallback,但是都不會執行,只會在空閑時期,去依次根據調用的順序就執行。