A:別急,咱們先來看一個簡單的場景:html
人人網刷朋友新鮮事你應該用過吧?實現這樣的功能有一種簡單的方式,是讓用戶與服務器之間保持一個長輪詢。html5
可是它與普通的 Ajax 不同,服務器不會立馬返回信息,它會先 hold 住,等待應該返回信息了,它纔會返回信息(好比你的好友發了一條新的狀態)。node
從傳統服務端來看(好比 Apache),每次一個新用戶連到你的網站上,你的服務器得新開一個鏈接,每一個鏈接都須要佔用一個線程,這些線程大部分都是閒着的(好比等你的好友髮狀態,查數據庫等),雖然它們是閒着的,可是照樣佔用了內存,也就是說,若是用戶達到必定的規模,服務器的內存就會耗光而癱瘓。web
解決辦法有不少,好比說使用線程池,可是它依然是阻塞的,若是線程池裏的全部線程都被阻塞(網速慢,被人惡意暫用)那麼接下來的請求將會排隊等待。數據庫
Node.js 就不相同了,它使用了「非阻塞」與「事件驅動」模型,你能夠把它想象成一個 Event Loop 循環,這個循環會一直跑。一個新的請求來了,Event Loop 接收這個請求,而後交給其餘線程,好比查詢數據庫,而後響應一個 callback,接着接收其餘請求,而不是等待數據庫結果的返回。api
若是數據庫返回告終果,服務端將會把它返回給客戶端,並繼續循環。這就是事件驅動:服務端只在有事情發生時,纔會有相應的處理(或者是接受請求,或者是一些 callback)。promise
A:是的,簡單來說,Node.js 的 Event Loop 是基於 libuv,而瀏覽器的 Event Loop 則是在 html5 規範 中定義,具體實現交給瀏覽器廠商。瀏覽器
A:對比來看,它們有點類似:服務器
在瀏覽器中比較簡單,值得注意的一點是,會在每一個 tasks 以後,會把當前 microtask 隊列裏的任務都執行完:app
Node.js 稍微複雜一點,每次 Event Loop 都須要通過六個階段,每個階段以後,都會執行 nextTick、microtasks (resolved promise, 等):
┌───────────────────────┐
┌─>│ timers │ <─── setTimeout/setInterval callback
│ └──────────┬────────────┘ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <─────────────── │ │
│ │ │ microTask queue │
│ ┌──────────┴────────────┐ └─────────────────────────┘
│ │ I/O callbacks │
│ └──────────┬────────────┘ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <─────────────── │ │
│ │ │ microTask queue │
│ ┌──────────┴────────────┐ └─────────────────────────┘
│ │ idle, prepare │ <─── 僅內部使用
│ └──────────┬────────────┘
│ │ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <──────────────── │ │
│ │ │ microTask queue │
│ │ └─────────────────────────┘
│ │ ┌─────────────────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │ <────┤ connections, │
│ └──────────┬────────────┘ │ data, etc │
│ │ └─────────────────────────┘
│ │ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <──────────────── │ │
│ │ │ microTask queue │
│ │ └─────────────────────────┘
│ ┌──────────┴────────────┐
│ │ check │ <─── setImmediate callback
│ └──────────┬────────────┘ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <─────────────── │ │
│ │ │ microTask queue │
│ ┌──────────┴────────────┐ └─────────────────────────┘
│ │ close callbacks │ <─── eg: socket.on("close",func)
│ └──────────┬────────────┘ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <─────────────── │ │
└─────────────┴ │ microTask queue │
└─────────────────────────┘
複製代碼
來一段簡單的代碼,猜猜瀏覽器(Chrome)和 Node.js 分別輸出什麼:
console.log('start');
setTimeout(() => {
console.log('timer1');
Promise.resolve().then(() => {
console.log('promise1');
});
}, 0);
setTimeout(() => {
console.log('timer2');
Promise.resolve().then(() => {
console.log('promise2');
});
}, 0);
console.log('end');
複製代碼
A:咱們先來驗證一下:
瀏覽器中:
start
end
timer1
promise1
timer2
promise2
複製代碼
Node.js 中:
start
end
timer1
timer2
promise1
promise2
複製代碼
看來和想象中的不同,別急,看個動圖就會明白了:
瀏覽器中:
Node.js 中:
setTimeout callback
里加上 process.nextTick
那麼是比 Promise.then
先執行?A:是的,還記得上面所說過的嗎,在每一個階段後都會執行 nextTick queue 以及 micktasks queue,nextTick queue 的優先級比 micktasks queue 高。
A:...
未完待續...
注:Node v11+ 版本後,運行的結果與瀏覽器中一致。