在講 Event Loop (事件循環(huán))之前猎拨,我們來了解點(diǎn) node 的東西,來幫助我們更加明白事件循環(huán)是干什么的
Node 是什么
Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境凹联,Node 不是一門語言,是讓 js 運(yùn)行在后端的,運(yùn)行時不包括 js 全集未辆,因?yàn)樵诜?wù)端中不包含 DOM 和 BOM,Node 也提供了一些新的模塊锯玛,比如 http咐柜,fs等模塊。
Node 解決了什么
Node 的首要目標(biāo)是提供一種簡單的攘残,用于創(chuàng)建高性能服務(wù)器的開發(fā)工具
Web 服務(wù)器的瓶頸在于并發(fā)的用戶量拙友,對比 Java 和 Php 的實(shí)現(xiàn)方式
Node在處理高并發(fā),I/O 密集場景有明顯的性能優(yōu)勢
- 高并發(fā),是指在同一時間并發(fā)訪問服務(wù)器
- I/O 密集指的是文件操作歼郭、網(wǎng)絡(luò)操作遗契、數(shù)據(jù)庫,相對的有 CPU 密集,CPU 密集指的是邏輯處理運(yùn)算、壓縮病曾、解壓牍蜂、加密、解密
Web 主要場景就是接收客戶端的請求讀取靜態(tài)資源和渲染界面,所以 Node 非常適合 Web 應(yīng)用的開發(fā)泰涂。
進(jìn)程與線程
進(jìn)程是操作系統(tǒng)分配資源和調(diào)度任務(wù)的基本單位鲫竞,線程是建立在進(jìn)程上的一次程序運(yùn)行單位,一個進(jìn)程上可以有多個線程逼蒙。
- 瀏覽器線程
- 用戶界面-包括地址欄从绘、前進(jìn)/后退按鈕、書簽菜單等
- 瀏覽器引擎-在用戶界面和呈現(xiàn)引擎之間傳送指令(瀏覽器的主進(jìn)程)
- 渲染引擎是牢,也被稱為瀏覽器內(nèi)核(瀏覽器渲染進(jìn)程)
- 一個插件對應(yīng)一個進(jìn)程(第三方插件進(jìn)程)
- GPU提高網(wǎng)頁瀏覽的體驗(yàn)( GPU 進(jìn)程)
- 瀏覽器渲染引擎
- 渲染引擎內(nèi)部是多線程的顶考,內(nèi)部包含 ui 線程和 js 線程
- js 線程 ui 線程 這兩個線程互斥的,目的就是為了保證不產(chǎn)生沖突妖泄。
- ui 線程會把更改的放到隊(duì)列中驹沿,當(dāng) js 線程空閑下來的時候,ui 線程在繼續(xù)渲染
- js 單線程
- js 是單線程蹈胡,為什么呢渊季?如果多個線程同時操作 DOM 朋蔫,哪頁面不會很混亂?這里所謂的單線程指的是主線程是單線程的,所以在 Node 中主線程依舊是單線程的却汉。
- webworker 多線程
- 它和 js 主線程不是平級的驯妄,主線程可以控制 webworker,但是 webworker不能操作 DOM合砂,不能獲取 document青扔,window
- 其他線程
- 瀏覽器事件觸發(fā)線程(用來控制事件循環(huán),存放 setTimeout、瀏覽器事件翩伪、ajax 的回調(diào)函數(shù))
- 定時觸發(fā)器線程(setTimeout 定時器所在線程)
- 異步 HTTP 請求線程(ajax 請求線程)
單線程特點(diǎn)是節(jié)約了內(nèi)存,并且不需要在切換執(zhí)行上下文微猖。而且單線程不需要管鎖的問題,所謂 鎖,在 java 里才有鎖的概念缘屹,所以我們不用細(xì)研究
瀏覽器中的 Event Loop
- 同步任務(wù)都在主線程上執(zhí)行凛剥,形成一個執(zhí)行棧
- 主線程之外,還存在一個任務(wù)隊(duì)列轻姿。只要異步任務(wù)有了運(yùn)行結(jié)果犁珠,就在任務(wù)隊(duì)列之中放置一個事件。
- 一旦執(zhí)行棧中的所有同步任務(wù)執(zhí)行完畢互亮,系統(tǒng)就會讀取任務(wù)隊(duì)列,將隊(duì)列中的事件放到執(zhí)行棧中依次執(zhí)行
- 主線程從任務(wù)隊(duì)列中讀取事件犁享,這個過程是循環(huán)不斷的
整個的這種運(yùn)行機(jī)制又稱為 Event Loop (事件循環(huán))
node 中的 Event Loop
如圖(圖片是借鑒的):
* 我們寫的代碼會交給 V8 引擎去進(jìn)行處理
* 代碼中可能會調(diào)用 nodeApi,node 會交給 LIBUV 庫處理
* LIBUV 通過阻塞 i/o 和多線程實(shí)現(xiàn)了異步 io
* 通過事件驅(qū)動的方式,將結(jié)果放到事件隊(duì)列中,最終交給我們的應(yīng)用豹休。
同步炊昆,異步 阻塞和非阻塞
- 阻塞和非阻塞指的是調(diào)用者的狀態(tài),關(guān)注的是程序在等待調(diào)用結(jié)果時的狀態(tài)
- 同步和異步指的是被調(diào)用者是如何通知的慕爬,關(guān)注的是消息通知機(jī)制
宏任務(wù)和微任務(wù)
- macro-task(宏任務(wù)):
- setTimeout, setInterval, setImmediate, I/O
- micro-task(微任務(wù)):
- process.nextTick,
- 原生 Promise (有些實(shí)現(xiàn)的promise 將 then 方法放到了宏任務(wù)中,瀏覽器默認(rèn)放到了微任務(wù)),
- Object.observe (已廢棄),
- MutationObserver(不兼容屏积,已廢棄)
- MessageChannel(vue中 nextClick 實(shí)現(xiàn)原理)
同步代碼先執(zhí)行医窿,執(zhí)行是在棧中執(zhí)行的,微任務(wù)大于宏任務(wù)炊林,微任務(wù)會先執(zhí)行(棧)姥卢,宏任務(wù)后執(zhí)行(隊(duì)列)
講到這里,敲幾行代碼來總結(jié)下我們上面講到的知識點(diǎn)把
《1》宏任務(wù)渣聚,微任務(wù)在瀏覽器和 node 環(huán)境執(zhí)行順序不同
// 這個列子里面独榴,包含了宏任務(wù),微任務(wù)奕枝,分別看看瀏覽器和node 打印的結(jié)果
console.log(1)
// 棧
setTimeout(function(){
console.log(2)
// 微任務(wù)
Promise.resolve(100).then(function(){
console.log('promise')
})
})
// 棧
let promise = new Promise(function(resolve, reject){
console.log(7)
resolve(100)
}).then(function(data){
// 微任務(wù)
console.log(data)
})
// 棧
setTimeout(function(){
console.log(3)
})
console.log(5)
// 瀏覽器結(jié)果:1 7 5 100 2 promise 3
// node 結(jié)果: 1 7 5 100 2 3 promise
瀏覽器和 node 環(huán)境執(zhí)行順序不同棺榔,瀏覽器是先把一個棧以及棧中的微任務(wù)走完,才會走下一個棧隘道。node 環(huán)境里面是把所以棧走完症歇,才走微任務(wù)
《2》setTimeout setImmediate 都是宏任務(wù)郎笆,誰優(yōu)先執(zhí)行呢?
setTimeout(function(){
console.log('timeout')
})
setImmediate(function(){
console.log('setImmediate')
})
// 結(jié)果打油睢:timeout setImmediate
setTimeout setImmediate 這兩個取決于 node 的執(zhí)行時間
《3》nextTick 和 then 都屬于微任務(wù)宛蚓,誰優(yōu)先執(zhí)行呢?
process.nextTick(function(){
console.log('nextTick')
})
Promise.resolve().then(function(){
console.log('then')
})
// 結(jié)果打由杷:nextTick then
// 再加一個宏任務(wù)呢
setImmediate(function(){
console.log('setImmediate')
})
// 結(jié)果打悠嗬簟:nextTick then setImmediate
nextTick 會比 其他微任務(wù)、宏任務(wù)執(zhí)行快
《4》i/o 文件操作(宏任務(wù))闰蛔,搭配微任務(wù)痕钢,誰優(yōu)先執(zhí)行呢?
let fs = require('fs');
fs.readFile('./1/log',function(){
console.log('fs')
})
process.nextTick(function(){
console.log('text')
})
// 結(jié)果打映ぁ:text fs
i/o 文件操作(宏任務(wù)), 如果有微任務(wù)盖喷,先執(zhí)行微任務(wù),在執(zhí)行文件讀取