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
你答對了嗎?