1.node是什麼?
node是一個基於Chrome V8引擎的javascript執行環境,node不是一門語言,而是讓javascript執行在後端的執行時環境,所以node中沒有DOM和BOM,node也提供了一些內建模組,例如:http,fs等。nodejs使用了事件驅動、非阻塞式I/O的模型,使其輕量又高效。
2.程式和執行緒
一個程式中可以包含多個執行緒。例如瀏覽器的渲染引擎,其實就是瀏覽器核心,它內部就是多執行緒的,內部包含兩個非常重要的執行緒UI執行緒和JS執行緒,特別要注意的是UI執行緒和JS執行緒是互斥的,因為JS執行結果會影響到UI執行緒的結果,那麼UI更新會被儲存在佇列中,等到JS執行緒空閒時,立即被執行。
3.瀏覽器中的Event Loop(事件迴圈)
我們從一道面試題,說明一下瀏覽器的event loop:
console.log(1);
setTimeout(function(){
console.log(2);
});
console.log(3);
複製程式碼
我們大家都知道,執行結果是1,3,2。但是,瀏覽器到底是怎麼執行的吶?
因為console.log(1)和console.log(3)是同步任務,所以將它們放到執行棧中去執行,js引擎在遇到setTimeout時,會把它當作非同步任務,不會把它放到執行棧中去執行,瀏覽器的timer模組會把setTimeout先拿走,時間到了,timer模組會把它放到非同步佇列中去,js引擎發現執行棧中沒有要執行的東西了,就會讀取非同步佇列中的內容,放到執行棧中去執行,這時setTimeout中的函式體就變成執行棧中的同步任務,執行完後,再去監聽非同步佇列中有沒有,如果有繼續執行,如此迴圈,這個迴圈的過程就是event loop。
4.node系統
我們寫的js程式碼會交給V8引擎進行處理;程式碼中可能呼叫node API,node會交給libuv庫處理;libuv通過阻塞I/O和多執行緒實現了非同步I/O;通過事件驅動的方式將結果放到事件佇列中,最終交給我們的應用。
5.巨集任務和微任務
任務分為巨集任務和微任務。
macro-task(巨集任務):setTimeout,setInterval,setImmediate,I/O
micro-task(微任務):原生Promise,process.nextTick,MessageChannel(vue中nextTick實現原理)
在瀏覽器中,先執行當前棧,執行完後,再走微任務,微任務執行完後,再去取事件佇列中的內容。
console.log(1);
console.log(2);
setTimeout(function(){
console.log(`setTimeout1`);
Promise.resolve().then(function(){
console.log(`promise`);
});
});
setTimeout(function(){
console.log(`setTimeout2`);
});
複製程式碼
瀏覽器中的執行結果是:
1
2
setTimeout1
promise
setTimeout2
node中的執行結果:
1
2
setTimeout1
setTimeout2
promise
我們發現,同樣的程式碼,在瀏覽器中執行和在node中執行的結果不同,這是為什麼吶?接下來,我就要說一下node中的event loop。
在libuv內部,有這樣一個事件環機制,在node啟動時,會初始化事件環。
這裡每一個階段都對應一個事件佇列,當event loop執行到某個階段時,會將當前階段對應的佇列依次執行完。當佇列執行完畢或執行的數量超過上限時,會轉入下一個階段。
那就說一下上題,在node中的執行的詳細過程吧。
首先,將console.log(1)和console.log(2)放到執行棧中去執行,執行完後,會看一下是否有微任務,如果有,會執行微任務(微任務執行,都會在階段轉換時被執行),那麼現在沒有,接著往下執行,遇到兩個setTimeout,會將它們放到第一階段timers(計數器)中,接著往下一個階段執行,當setTimeout到時間了,會將到時間的setTimeout都執行完畢,再去執行微任務,當執行第一個setTimeout時,遇到了一個Promise微任務,會將它放到微任務中,然後,執行第二個setTimeout,都執行完後,再去執行微任務。
我們再看幾個題,能夠更好的理解node中的event loop。
process.nextTick(function(){
console.log("nextTick");
});
setImmediate(function(){
console.log("immediate");
});
複製程式碼
node中的執行結果,毫無疑問是
nextTick
immediate
let fs = require(`fs`);
fs.readFile(`./1.log` , function(){
console.log(`fs`);
setTimeout(function(){
console.log(`timeout`);
});
setImmediate(function(){
console.log(`immediate`);
});
});
複製程式碼
node中的執行結果是:
fs
immediate
timeout
因為I/O操作完了,會走check階段,所以setImmediate會早於setTimeout。
再看一下,最後一道題,這道題你答對了,說明你真的明白了node中的event loop了。
setImmediate(function(){
console.log(1);
process.nextTick(function(){
console.log(4);
});
});
process.nextTick(function(){
console.log(2);
setImmediate(function(){
console.log(3);
});
});
複製程式碼
node中的執行結果:
2
1
3
4
你答對了嗎?